Ce n’est pas dans mon habitude de poster des conseils sur des outils et leur configuration, mais cette fois je vais faire une petite entorse au règlement; tout simplement car j’ai pas mal galéré pour résoudre ce soucis, et si ce post permet d’aider une seule personne alors cela en aura valu le coup.
Bref, depuis la version 1.7 le niveau de sécurité de Mercurial à été revu à la hausse, et donc évidement des petits tracas pour nous utilisateurs de Tortoise HG & Visual HG sous Windows. En résumé, les connexions HTTPS utilisant des certificats auto signés (Self-signed certificates) ne sont plus acceptés par le controleur de code source.
La documentation stipule qu’il est possible de rajouter dans le fichier cacert.pem de Tortoise le certificat que vous souhaitez autoriser; Le problème est qu’il va vous falloir utiliser openssl, non présent en standard sur un Windows. Je vous avouerais que je n’ai même pas tenté de le télécharger et de l’utiliser; Alors peut-être est-ce facilement faisable sous Windows… personnellement j’avais peur de me lancer dans une galère de plus.
Par contre, laissez-moi vous montrer une façon de faire beaucoup plus simple, pour nous Windowsien de base et tout aussi sécurisée.
L’idée est d’ajouter l’empreinte numérique du serveur hébergeant le repository dans la configuration de Tortoise; la doc étant un peu légère sur ce point, voici les étapes pour y arriver.
Bon, tout d’abord ouvrez votre navigateur préféré et rendez-vous sur la home page de votre repository. En cliquant sur le petit cadenas en bas de votre fenêtre vous devriez avoir une fenêtre comme celle-ci qui s’ouvre :
Le bouton “afficher certifat” ouvre une nouvelle fenêtre dans laquelle vous copierez l’empreinte numérique SHA1. Cette empreinte identifie de manière unique le serveur hebergeant votre repository Mercurial.
Ensuite, ouvrez Tortoise hg Workbench, puis dans la fenêtre de configuration, éditez le fichier de configuration mercurial.ini.
Il ne reste plus qu’à ajouter une section hostfingerprints reprenant l’adresse de votre server ainsi que son empreinte.
Comme beaucoup d’entre vous j’utilise l’excellent SyntaxHighlighter d’Alex Gorbatchev, pour mettre en forme les snippets de code sur mon blog.
Bien qu’il dispose de nombreux plugin de prise en charge de language, parfois très ésotérique, je n’ai encore rien vu pour le language préféré de tous ceux qui font du MVC 3 ou du WebMatrix, j’ai nommé razor.
Du coup, je me suis lancé et voici une toute première version téléchargeable ici
et voici un exemple de mise en forme
@model CustomMVCScaffolder.Models.Employee
@* This partial view defines form fields that will appear when creating and editing entities *@
@Html.LabelFor(model => model.NationalIdNumber)
@Html.EditorFor(model => model.NationalIdNumber)
@Html.ValidationMessageFor(model => model.NationalIdNumber)
@foreach (var ourItem in ourList) {
if (ourItem != ourList.First()) {
outItem.Name
}
}
@Html.ActionLink("Edit", "Edit", new { id=item.UserName }) |@Html.ActionLink("Details", "Details", new { id=item.UserName }) |@Html.ActionLink("Delete", "Delete", new { id=item.UserName })
@Html.Hidden("UserName", Model.UserName)
Voici comment l’utiliser :
@Html.Hidden("UserName", Model.UserName)
J’espère que cette petite contribution à la communauté viendra enjoliver les snippets de code que l’on voit ça et là.
Ho… j’oubliais, si vous avez des propositions pour améliorer la mise en forme, je suis preneur.
On a tous connu ce cas de figure, ou l’on doit déployer une application web sur une plateforme autre que celle sur laquelle on développe (serveur de dev, recette, prod etc…);
A minima on se retrouve à modifier les chaines de connexions vers la/les bases de données, au mieux on a prévu une copie tweakée du web.config par plateforme (avec le gros inconvénient d’avoir maintenant plusieurs fichiers à maintenir en parallèle), on fait ça dans l’urgence et hop ça plante :p.
Surement un petit bout de configuration non que l’on a oublié de reporté dans ce fichier de conf spécifique à l’environnement visé. Ça sent le vécu, tout ça !
Réjouissez-vous ce temps est révolu!
Dans Visual Studio 2010, il possible de mettre en œuvre des transformations sur les fichiers de configuration. L’idée est d’avoir un fichier web.config servant de base, puis pour chaque Configuration de Solution (Debug, Release, etc..) un fichier de transformation contient les modifications apportés vis à vis du fichier de base.
Si l’on ajoute deux Configurations de solutions, par exemple Recette et Prod, puis que l’on clique sur Add Config Transform deux nouveaux fichiers apparaissent Web.Recette.config & Web.Prod.config
L’idée de ce billet n’est pas d’expliquer en détail la syntaxe de transformation bien expliquée sur MSDN; mais afin de poursuivre l’exemple voici mon web.config initial
et la transformation pour l’environnement de recette :
Bon maintenant la mauvaise nouvelle (haaa ? j’vous avais pas dit qu’il y avait une mauvaise nouvelle?), c’est que la transformation du fichier de config ne se fait pas lors du build.
Malheureusement, elle n’a lieu que lorsqu’on utilise webDeploy.
La bonne nouvelle? C’est qu’en trafiquant un peu le csproj on peut lancer la tache MSBuild en charge de la transformation.
Allez tout en bas du fichier et modifiez-le comme suit :
A partir de maintenant après chaque build le fichier web.config sera transformé et copié dans le répertoire obj/Nom_De_La_Config/web.config
Voilà, c’est tout pour ce soir, j’espère que cette astuce vous facilitera la vie lors de vos futurs déploiements.
alors au runtime, le framework choisira le composant graphique HTML (Input, select etc…) le plus adapté à afficher les propriétés du model et ca sur toutes vos vues.
La bonne nouvelle, c’est que l’on peut créer nous même des templates pour les nouveaux types et encore mieux modifier les templates liés aux types de base (int, string, DateTime etc..)
Stop le blabla, place au Datepicker.
Ok, on commence par créer dossier un EditorTemplates sous Views/Shared et on y place une nouvelle vue que l’on nommera DateTime.cshtml.
Remarquez la simplicité, on ne fait qu’appeler une méthode d’extension (que nous allons étudier de suite) et on formatte très légèrement la date (ToShortDateString).
Rajoutons une classe dans notre projet comportant le code suivant :
using System.Collections.Generic;
using System.Web.Mvc;
namespace MVCTest
{
public static class JQueryHtmlHelpers
{
public static MvcHtmlString Datepicker(this HtmlHelper htmlHelper, string name, object value)
{
return System.Web.Mvc.Html.InputExtensions.TextBox(htmlHelper, name, value, new Dictionary { { "class", "datepicker" } });
}
}
}
Dans cette méthode d’extension, on se contente de demander le rendu d’une TextBox (input html) a laquelle on applique la class css datepicker.
Vous l’aurez compris il ne nous reste plus qu’a écrire un peu de JQuery dans la master page pour que toute les texbox du site ayant la class datepicker se transforment par magie en DatePicker.
Avant-hier, mon client m’a demandé s’il était possible d’avoir une “cartographie” de sa base OLAP que nous sommes en train de construire.
Par cartographie, il entendait a minima avoir un listing de toutes les mesures, dimensions et niveaux existants. Ma première réaction fut de vérifier si BIDS Helper n’avait pas ce genre de feature cachée dans un coin. Circulez rien à voir de très intéressant de ce côté-là!
Je me suis ensuite rué sur Google pour voir s’il n’y avait pas un SQL Server Report Pack comme celui qui était apparu pour SQL Server 2005, mais axé Analysis Services. Rien non plus en direct de chez Redmond.
Par contre en creusant un peu je suis tombé sur l’excellent blog de la société Capstone et leur superbe Analysis Services 2008 Metadata Report Pack.
Voici un aperçu du rendu des rapports Reporting Services mis à disposition :
Et pour faire plaisir à mon ami François, il y a même des infos sur le Data Mining :
Pour installer le pack il vous faudra créer un serveur lié dans SQL Server; Voici le script SQL a mettre en adéquation avec la configuration de votre machine :
Puis déployer les rapports sur Reporting Services.
Je me permets de reposter l’ensemble de la solution de Dan Meyersici; version dans laquelle j’ai corrigé les connexions en dur par une connexion vers le serveur lié.
En dehors de répondre au besoin ponctuel de mon client, je me dis que ces rapports pourraient très bien trouver leur place dans une documentation technique à réaliser en fin de projet.
Dernièrement j’ai échangé avec mon Boss, Jérôme, sur les possibilités d’enrichir un site web non dévellopé en .NET mais qui serait hosté dans IIS.
On pourrait se mettre à apprendre le Python… mais voila on est un peu fainéant :)
Une des solutions que je lui ai proposé étais de tirer parti du mécanisme d’extensibilité du pipeline de IIS7 afin d’intervenir directement sur le code HTML renvoyé au navigateur.
Je me propose donc de présenter dans les grandes lignes comment mettre en place ce genre de solution.
Pour illustrer mes propos simplement nous allons nous rajouter un div en fin de page, le div contenant l’heure du système.
On commence par créer notre HTTPModule et on répond à l’évenement BeginRequest :
using System;
using System.IO;
using System.Web;
namespace InjectorModule
{
public class Injector : IHttpModule
{
private HttpApplication _application;
public void Init(HttpApplication context)
{
context.BeginRequest += OnBeginRequest;
_application = context;
}
private void OnBeginRequest(object sender, EventArgs e)
{
Stream filter = FilterFactory.GetFilter(_application);
if (filter == null)
return;
_application.Response.Filter = filter;
}
public void Dispose() {}
}
}
On délègue à une factory le role de créer ou non un filter en fonction de règles qui ne sont pas connues par le Module.
Le module reste ainsi une brique uniquement technique technique.
Passons à la factory
using System.IO;
using System.Web;
namespace InjectorModule
{
internal staticclass FilterFactory
{
public static Stream GetFilter(HttpApplication application)
{
if (application == null || application.Response.ContentType == null)
return null;
if (application.Response.ContentType.ToUpperInvariant().Contains("HTML"))
return new AppendDateTimeFilter(application.Response.Filter, application.Request.ContentEncoding);
return null;
}
}
}
Ici pas de règles métiers compliquées, on ajoute un Filter si le content type est de type HTML.
Et finalement voici l’implémentation du Filter qui tranforme la réponse envoyée aux navigateurs :
using System;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
namespace InjectorModule
{
internal class AppendDateTimeFilter : Stream
{
private readonly Stream _inputStream;
private readonly Encoding _encoding;
private readonly StringBuilder _responseHtml;
public AppendDateTimeFilter(Stream input, Encoding contentEncoding)
{
_inputStream = input;
_encoding = contentEncoding;
_responseHtml = new StringBuilder();
}
#region Filter overrides
public override bool CanRead
{
get { return true; }
}
public override bool CanSeek
{
get { return true; }
}
public override bool CanWrite
{
get { return true; }
}
public override void Close()
{
_inputStream.Close();
}
public override void Flush()
{
_inputStream.Flush();
}
public override long Length
{
get { return 0; }
}
public override long Position { get; set; }
public override long Seek(long offset, SeekOrigin origin)
{
return _inputStream.Seek(offset, origin);
}
public override void SetLength(long length)
{
_inputStream.SetLength(length);
}
public override int Read(byte[] buffer, int offset, int count)
{
return _inputStream.Read(buffer, offset, count);
}
#endregion
public override void Write(byte[] buffer, int offset, int count)
{
try
{
string bufferContent = _encoding.GetString(buffer);
// Wait for the closing tag
Regex eof = new Regex("", RegexOptions.IgnoreCase);
_responseHtml.Append(bufferContent);
if (!eof.IsMatch(bufferContent))
return;
// Transform the response and write it back out
string finalHtml = _responseHtml
.Replace("", string.Format(@"
{0}
", DateTime.Now))
.ToString();
// Send.
byte[] data = _encoding.GetBytes(finalHtml);
_inputStream.Write(data, 0, data.Length);
}
catch (Exception) { }
}
}
}
Pour tester rien de plus simple, on compile l’assembly, on la place dans le dossier bin du site web que l’on veux trafiquer.
Dans notre cas, nous avons aussi du créer le dossier bin car non existant dans l’appli Web Python.
Le fait de placer l’assembly dans un dossier bin, est une contrainte technique imposée par IIS; je n’ai pas trouvé d’alternatives et a priorit il n’y a pas de configuration qui pourrait influer sur cette contrainte.
Finalement dans le gestionnaire IIS, rendez vous dans la section Modules de votre application Web.
En ouvrant la fenetre d’ajout de modules, votre assembly sera désormais présente; Sélectionnez la, et pointez votre navigateur sur ce dernier.
Tadam!!! l’heure s’affiche tout en bas :)
J’esperes que cette solutions vous ouvre de nouvelles possibilités.
Un dernier mot pour vous conseiller l’excellente librairie htmlagilitypack qui vous donnera les moyens de parser facilement un document HTML, même malformé.
Je dois être maudit ! Cela fait prés de 3 ans que sur tous les projets sur lesquels j’interviens, le sempiternel troll du Identity vs Guid revient me hanter.
Non, non, non… je ne suis pas un ayatollah de la base de donnée… je n’ai rien contre les GUID ! Mais pourtant j’ai quand même du mal à comprendre pourquoi on a tendance à en coller partout, même quand ca n’a aucun sens… et ca j’ai vraiment du mal…
Mettons notre casquette de dba (je sais… beurk !)
Le Guid c’est le mal (sauf pour la réplication ou l’on n’a pas le choix)… car :
en production, on a vu plus simple que d’écrire des jointures sur des choses aussi imbitables, et anti-mémorisable que B2F62BF0-5CE0-408c-B749-F37D76AF5629.
Les perfs sont juste pourries par rapports à celles que l’on obtiendrait avec un int. J’en entends déjà plus d’un ricaner et prêt à me soutenir le contraire….Bien ! Rendez-vous sur ce benchmark, et prenez même 10 minutes pour lancer le test chez vous… c’est flagrant !Ce qui est étonnant, c’est qu’un grand nombre de développeurs, y compris des gens très brillant, passent à côté de ce point… certain m’ont déjà soutenu le contraire… au secours !
**Mettons notre casquette de développeur : **
Le Guid c’est la meilleure invention depuis le garbage collector… ! Si si je fais mon gloubi-boulga et puis je referme le capot, ca marche à tout les coups… c’est génial le GUID.
Dans une architecture en couche, je peux générer des Id dans ma couche client, associer des entités entre elles… puis :
Si j’ai envie de les persister, pas de soucis et pas d’aller retour avec la le tiers de persistance (base de données), mes identifiants sont à coup sur uniques.
Si j’ai envie d’annuler tout ce que j’ai fais, bah je cache méthodiquement tout sous le tapis (lisez : je laisse travailler le garbage collector).
Tandis qu’un Int en tant qu’identifiant, qu’est ce que c’est embêtant… je crée une entité sur mon client mais tant que je n’ai pas fait d’aller retour avec ma base de donnée, je n’ai aucun moyen de savoir son identifiant…
Vous voulez associer un produit et sa catégorie de produit tout deux nouvellement crées ? Possible, mais on va devoir écrire un peu de code et faire des allers-retours avec la base.
Bref… vu d’en haut l’identity c’est chiant… on est d’accord.
PS : Pour ceux qui me diraient qu’on est pas obligé de faire des applications en couches séparées… bah…. ils ont raison, mais sur un projet un minimum important ca devient vite difficile à maintenir de tout coder dans la form (aussi bien web que win)… faut que vous jetiez un coup d’œil au TDD.
Voila, c’est la que commence à mon sens notre vrai travail. Il nous faut estimer les avantages et les inconvénients des deux approches, et faire un choix entre la performance pure de la base et grosso modo la vitesse de développement de l’application… en ces temps ou le mot d’agilité et dans la bouche de tous, le choix est vite fait… Promis m’sieur y’a quelques temps j’arrivais encore à faire le grand écart…
Bon tout ca c’est bien beau, mais alors quelqu’un pourrait m’expliquer pourquoi il m’arrive de tomber sur des bases qui sont truffées de GUID (seul type de donnée utilisé comme clé primaire des tables) lorsque celles-ci sont attaquées par une application en mode client-serveur ? Franchement, la, ca me dépasse !
Bon, aller pour ceux qui ont réussi à me lire jusqu’ici, voici quelques liens supplémentaires sur le sujet :
Les racines des problèmes que posent les GUID avec les index Clustered
la fonction SQL-SERVER NEWSEQUENTIALID() si on adore les GUID dans SQL et que l’on ne peut s’en passer.
Et le meilleur pour la fin, je vous laisse réfléchir sur la phrase suivante trouvée dans la MSDN :
Consider using the IDENTITY property when global uniqueness is not required, or when having a serially incrementing key is preferred.
Evidemment, si certain d’entre vous on envie de troller sur le sujet… les commentaires sont fait pour ca :)
Comme vous le savez sûrement, la version 5 de Resharper est disponible depuis quelques temps, évidement pour supporter la version 2010 de Visual Studio; même si le produit est installable sur un Visual Studio 2008. Le but de ce post, n’est pas juste de vous annoncer la mise à dispo d’un soft et d’en faire la pub, c’est vraiment pas mon genre, et encore moins le but de ce blog.
Bon retour en arrière de quelques mois, je tombe (presque) par hasard sur le page de licensing de Resharper (à l’époque version 4.5) et je me disais que ca serait sûrement un bon investissement; Après tout, un code écrit plus vite, et avec moins de bugs (Modified Closure), c’est bon pour mes clients, ma boite et puis pour moi aussi au final. Bref, plusieurs type de licences sont listées sur le site, dont une Licence Open Source GRATUITE…
Ouarf ouarf ouaf, s’écrit mon côté pessimiste…
Ça c’est de la bonne pub gratuite.
J’approfondis un peu, il est dit qu’il faut présenter son projet Open Source, prouver sa participation dans le projet et une commission décidera si une licence gratuite sera attribuée etc…
Bon ok, en gros derrière dois y avoir une vieille boite mail que plus personne ne lit, et de toute façon ca m’étonnerait que les mecs aient le temps et l’envie de faire autre chose que coder pour leurs softs… alors de la à aller jeter un œil sur les projets Open Source soumis, puis faire une commission faudrait pas pousser quand même
Je vous l’accorde, ce jour la je devais être en mode Christophe… un peu beaucoup bougon! :)
Bon, pas très grave tout ca, car très peu de temps après je démarre une nouvelle mission (mon client actuel), et il se trouve que Resharper fait partie du toolkit de tout les développeurs… Ouf, j’vais pouvoir faire illusion, et faire croire que je sais un peu programmer.
Pas mal de temps passe, la question de l’acquisition de Resharper n’est plus trop d’actualité, jusqu’à ce que les betas de la version 5 ne commencent a être mise à dispo. Et puis lorsque la version finale sort, je ne peux m’empêcher de l’installer, pour 30 jours seulement en version demo… sniff.
Alors, il y a moins d’une semaine, je me lance, et soumet à la fameuse commission mon pet projet actuel (Claymore), sans trop de conviction. Et figurez vous qu’a peine 3 jours après (un dimanche exactement… bah oui les commissions de développeurs c’est en dehors des heures de travail) la bonne nouvelle tombe dans ma boite mail… j’ai le droit a une version Full de Resharper 5.0 pendant 1 an grâce à ce projet Open Source.
Donc déjà, je vous demande pardon mesdames et messieurs de chez JetBrains d’avoir douté de votre parole. Merci de cet effort que vous consentez pour améliorer la qualité du code Open Source. Et puis, j’suis sur que mes clients actuels et futures vous remercieront aussi… j’ferais moins de bugs grâce a vous.
Moralité, si vous avez un bout de code qui vaut le coup d’être mis à dispo des autres, et qui de toute façon ne vous rendra pas millionnaire, publiez-le, il vous permettra peut être d’avoir une licence gratuite de Resharper.
**Update [28 Avril 2010] : **Jérôme m’a fait remarquer que mon lien vers l’exemple de Modified Closure était erroné. C’est corrigé.
En ce moment je fais joujou avec les opcodes CIL qui se cachent sous notre bon vieux C#.
En voulant surcharger et cacher (shadow) successivement deux propriétés hérités, j’ai remarqué que le compilo générait une suite de metadata assez déroutante. Prenons l’exemple suivant :
public class BaseClass
{
public int Prop1 { get; set; }
public virtual int Prop2 { get; set; }
public virtual int Prop3 { get; set; }
}
public class DerivClass : BaseClass
{
public int Prop1 { get; set; }
public new int Prop2 { get; set; }
public new virtual int Prop3 { get; set; }
}
Du coté de la classe de base on obtient les metadata suivantes sur le getter :
non virtual :
.method public hidebysig specialname instance int32 get_Prop1() cil managed
Oulaaa… c’est la que ca se corse!!
Dans la classe de base, comme je vous le faisait remarquer précédemment, le compilo utilise conjointement les metadata virtual et newslot. Logiquement, on aurait pu s’attendre a voir uniquement le virtual.
Dans la classe dérivée, le shadowing non virtuel
public new int Prop2 { get; set; }
produit des metadata token strictement identiques à une propriété normale; et le shadowing virtuel produit un IL identique à propriété virtuelle.
De ces constatation on peut en déduire que :
Le token newslot, ne suffit pas à affirmer qu’il s’agit d’une shadowing methode (le getter non-virtuel de la classe de base et le getter new de la classe dérivée ayant les mêmes metadata).
Par contre, si une méthode contient le token virtual mais pas le newslot, alors c’est qu’il s’agit a coup sur d’un overriding et pas un shadowing.
Par déduction, on peut donc penser que si une méthode n’est pas un overriding (le token newslot est présent), alors il n’y a shadowing que si la signature [type de retour, nom et paramètres de la méthode] de la méthode existe également dans la classe de base.
Au cours du développement de Claymore, pour la partie ASP.NET plus précisément, j’ai eu besoin de trouver un hook me donnant la possibilité d’interagir avec les pages avant les events que l’on trouve sur l’objet Page.
J’ai tout d’abord pensé tripatouiller dans le fichier Global.asax… sans finalement avoir trouvé ce que je souhaitais. Par contre, en fouillant un peu du côté des HttpModule je suis arrivé à la solution suivante :
using System;
using System.Reflection;
using System.Web;
using System.Web.UI;
namespace Claymore.Web
{
///
///
///
public class ClaymoreHttpModule : IHttpModule
{
#region Fields
private HttpApplication _application;
#endregion
///
/// Initializes a module and prepares it to handle requests.
///
///
An that provides access to the methods, properties, and events common to all application objects within an ASP.NET application
public void Init(HttpApplication context)
{
if(context == null)
throw new ArgumentNullException("context");
_application = context;
context.PostMapRequestHandler += onPostMapRequestHandler;
}
///
/// Disposes of the resources (other than memory) used by the module that implements .
///
public void Dispose(){}
///
/// Handle PostMapRequest event.
///
/// The sender.
/// The instance containing the event data.
private void onPostMapRequestHandler(object sender, EventArgs e)
{
Page pageHandler;
if ((pageHandler = _application.Context.Handler as Page) != null)
pageHandler.PreInit += HandlePreInit;
}
///
/// Handles the pre init event.
///
/// The sender.
/// The instance containing the event data.
protected virtual void HandlePreInit(object sender, EventArgs e)
{
Page page = sender as Page;
if (page != null){
// Et voila, on a un pointeur sur une page, et l'on peut interagir avec elle avant l'événement Init.
// Ajouter du code ici.
}
}
}
}
L’un d’entre vous connaîtrait-il une solution équivalente à base de HttpHandler ou autre?