Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

Atteint de JavaScriptite Aiguë [Cyril Durand]

Expert ASP.net Ajax et WCF, Cyril Durand parle dans son blog de point techniques sur ASP.net, ASP.net Ajax, JavaScript, WCF et .net en général. Cyril est également consultant indépendant, n'hésitez pas à le contacter pour de l'assistance sur vos projets

Actualités

  • Blog de Cyril DURAND, passionné de JavaScript, Ajax, ASP.net et tout ce qui touche au developpement Web Client-Side.

    N'hésitez pas à me contacter pour vos projets .net : architecture, accompagnement, formation, ...

    View Cyril Durand's profile on LinkedIn
    hit counters


    Expertise Commerce server et BizTalk

WebCast – Mise en oeuvre de Lab Management avec Team Foundation Server

Connaissez-vous la partie Lab Management de Team Foundation Server ?

Il s’agit d’une fonctionnalité de Team Foundation Server permettant de déployer et d’exécuter des tests UI de façon automatique sur un environnement dédié.

Si vous ne connaissez pas cette fonctionnalité et que vous souhaitez voir TFS manipuler des snapshot hyper-v, déployer une application sur plusieurs serveurs, jouer des tests UI et bien d’autres choses encore, le tout automatiquement. Avec l’aide de Guillaume Brout, je présenterais demain, à 10h, par WebCast, Lab Management.

Au programme :

image

Pour vous inscrire à ce webcast, suivez le lien : Mise en œuvre de Lab Management avec Team Foundation Server et Microsoft Test Manager

Posted: mercredi 21 mars 2012 15:34 par cyril | 0 commentaire(s)
Classé sous :
DataContractSerializer et DataContractResolver sérialisation d’un type abstrait sans KnownType

Lorsque l’on utilise le DataContractSerializer, il arrive que l’on souhaite sérialiser un type abstrait sans connaitre l’implémentation concrète.

Exemple :

[DataContract]
abstract class Foo
{ }
 
[DataContract]
class Bar : Foo
{ }
 
class Program
{
    static void Main(string[] args)
    {
 
        DataContractSerializer serializer = new DataContractSerializer(typeof(Foo));
        using (MemoryStream ms = new MemoryStream())
        {
            serializer.WriteObject(ms, new Bar());
        }
    }
}

Le code ci-dessus va déclencher une exception de type SerializationException avec comme message :

Type 'ConsoleApplication8.Bar' with data contract name 'Bar:http://schemas.datacontract.org/2004/07/ConsoleApplication8' is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer

Le problème est que le DataContractSerializer n’est pas capable de sérialiser un type s’il ne connait pas l’ensemble des types à sérialiser.

Une des solutions pour résoudre ce problème est d’utiliser les KnownType. Je vous avais d’ailleurs parlé des KnownType avec WCF il y a quelques temps (WCF : KnownType - Comment retourner des types enfants du type définit dans le ServiceContract)

Il existe plusieurs façons de faire:

  • soit en modifiant l’instanciation du DataContractSerializer :
DataContractSerializer serializer = new DataContractSerializer(typeof(Foo), new Type[] { typeof(Bar) });
  • soit en rajoutant des attributs
[DataContract]
[KnownType(typeof(Bar))]
abstract class Foo
{ }
  • soit en fournissant une méthode retournant les KnownTypes
[DataContract]
[KnownType("GetKnownTypes")]
abstract class Foo
{
    public static IEnumerable<Type> GetKnownTypes()
    {
        yield return typeof(Bar);
    }
}
<configuration>
  <system.runtime.serialization>
    <dataContractSerializer>
      <declaredTypes>
         <add type="ConsoleApplication8.Foo, ConsoleApplication8">
            <knownType type="ConsoleApplication8.Bar, ConsoleApplication8"/>
         </add>
      </declaredTypes>
    </dataContractSerializer>
  </system.runtime.serialization>
</configuration>

Ces solutions sont intéressantes mais elles nécessitent de connaitre le type concret, or, il peut arriver que l’on ne le connaisse pas.

Mais pourquoi le DataContractSerializer a besoin de connaitre l’ensemble des types qu’il va sérialiser ?

Le DataContractSerializer a besoin de connaitre l’ensemble des types pour plusieurs raisons. D’une part lors de la sérialisation, comment transmettre le type Bar à une autre application alors que celle-ci attends le type Foo ? D’autre part, lors de la désérialisation, que faire si l’on m’envoie un type Bar alors que j’attends un Foo? ? ignorer les membres qui ne sont pas de Foo ? lever une exception ? Ce sont quelques une des raisons pour lesquelle le DataContractSerializer nécessite de connaitre l’ensemble des types qu’il va sérialiser / déserializer.

Cependant, lorsque l’on regarde les différentes signatures du constructeur du DataContractSerializer, on voit une signature qui prend un DataContractResolver. Un DataContractResolver permet d’expliquer au DataContractSerializer comment sérialiser/déserialiser un type.

Le type DataContractResolver est une classe abstraite possédant 2 méthodes :

  • ResolveName : permet de retrouver un type à partir d’un String
  • TryResolveType : permet de retourner un String à partir d’un type

Ce qui nous donne le code suivant :

/// <remarks>May not correctly if an assembly is signed and 2 version of the same type live in the AppDomain</remarks>
public class LazyContractResolver : DataContractResolver
{
  private static Regex URI_REGEXP = new Regex("^uri:(.*),(.*)$", RegexOptions.Compiled);
 
  public override Type ResolveName(String typeName, String typeNamespace, Type declaredType, 
                   DataContractResolver knownTypeResolver)
  {
    Type t = knownTypeResolver.ResolveName(typeNamespace, typeNamespace, declaredType, knownTypeResolver);
    if (t == null && typeNamespace.StartsWith("uri:"))
    {
      Match match = LazyContractResolver.URI_REGEXP.Match(typeNamespace);
      if (!match.Success)
      {
        throw new ArgumentException(String.Format("uri is not valid. uri : '{0}'", typeNamespace));
      }
      String namespaceName = match.Groups[1].Value;
      String assemblyName = match.Groups[2].Value;
 
      t = Type.GetType(String.Format("{0}.{1},{2}", namespaceName, typeName, assemblyName));
    }
    return t;
  }
 
  public override Boolean TryResolveType(Type type, Type declaredType, DataContractResolver knownTypeResolver, 
                      out XmlDictionaryString typeName, out XmlDictionaryString typeNamespace)
  {
    Boolean isTypeResolved = knownTypeResolver.TryResolveType(type, declaredType, null, out typeName, out typeNamespace);
    if (!isTypeResolved)
    {
      XmlDictionary dictionary = new XmlDictionary();
      typeName = dictionary.Add(type.Name);
      typeNamespace = dictionary.Add(String.Format("uri:{0},{1}", type.Namespace, type.Assembly.GetName().Name));
      isTypeResolved = true;
    }
    return isTypeResolved;
  }
}

Voici un exemple permettant d’utiliser le LazyContractResolver :

[DataContract]
abstract class Foo
{ }
 
[DataContract]
class Bar : Foo
{ }
 
class Program
{
  static void Main(string[] args)
  {
    DataContractSerializer serializer = 
      new DataContractSerializer(typeof(Foo), null, int.MaxValue, true, true, 
                                 null, new LazyContractResolver());
    using (MemoryStream ms = new MemoryStream())
    {
      serializer.WriteObject(ms, new Bar());
    }
  }
}

Et vous, avez-vous déjà été confronté à des problèmes de KnownTypes ? Avez-vous déjà utilisé un DataContractResolver ?

IIS7 – Compression GZIP

La compression GZIP permet d’améliorer les performances de navigation en compressant ce qu’envoie le serveur à un client.

Pour comprendre comment cela fonctionne, regardons ce qu’il se passe au niveau HTTP lorsqu’un client tente d’accéder à une ressource sur un serveur distant.

Tout d’abord, le client va emettre une requête HTTP, cette requête va contenir plusieurs informations tels que l’uri demandé, le User-Agent, etc.

GET http://localhost/GZIPTest/ HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Accept-Language: en-US
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
Host: localhost

Parmi ces informations, il y a un header HTTP nommé Accept-Encoding. Cette valeur indique au serveur quelles sont les formats de fichier que le client est capable de lire. Dans l’exemple ci-dessus, on voit que le client est capable de lire du gzip et du deflate.

Dans ce cas, lorsque le serveur va renvoyer la réponse, il va renvoyer différentes en tête HTTP puis le contenu de la réponse gzippé. Parmi les en-têtes HTTP renvoyés, l’entête Content-Encoding indiquant le format de la réponse.

HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Content-Encoding: gzip
Vary: Accept-Encoding
Server: Microsoft-IIS/7.5
X-Powered-By: ASP.NET
Date: Tue, 31 Jan 2012 13:56:08 GMT
Content-Length: 279

Avoir une réponse compressée permet de grandement diminuer la taille des échanges. Si les clients le supportent, il est fortement recommandé de toujours envoyer une réponse gzipée.

Mais comment activer la compression GZIP avec IIS ?

La compression gzip est une fonctionnalité de IIS7 mais n’est pas installé par défaut. Vous pouvez l’installer soit en modifiant l’installation de IIS7 par le server manager, soit en utilisant WebPI.

image

Il existe 2 fonctionnalités distinctes : la compression statique et la compression dynamique. La compression statique permet de compresser les fichiers statiques tels que les .css, .js, etc. alors que la compression dynamique permet de compresser les fichiers dynamiques tels que les fichiers .aspx, etc.

Après avoir installé ces modules, il est ensuite nécessaire de les activer. Dans la console d’administration IIS, une “feature” compression est présente. Lorsque l’on ouvre cette feature, on peut activer ou non la compression statique et/ou dynamique.

image

image

Il existe évidemment des différences entre les 2 modes.

La compression statique est moins couteuse que la compression dynamique, elle est faite qu’une seule fois puis le résultat de la compression est mise en cache. Par défaut, le cache des fichiers compressé se trouvent dans %SystemDrive%\inetpub\temp\IIS Temporary Compressed Files. Si vous avez un grand nombre de fichier et ou si vous avez un espace disque faible, il est recommandé de déplacer ce dossier. Pour cela, vous pouvez le configurer via l’attribut directory de la section system.WebServer/httpCompression.

De plus, par défaut, IIS limite la taille du dossier de cache à 100Mo par pool d’application. L’attribut maxDiskSpaceUsage permet de modifier cette limite, la valeur s’exprime en Mo, il est également possible de désactiver cette limite en mettant l’attribut doDiskSpaceLimiting à false (ce que je ne recommande pas).

<configuration>
  <system.WebServer>
    <httpCompression directory="%SystemDrive%\inetpub\temp\IIS Temporary Compressed Files" maxDiskSpaceUsage="100" 
doDiskSpaceLimiting="true"
>

Les pages dynamiques étant théoriquement unique, il n’est pas possible de mettre en cache le résultat de la compression, les réponses sont donc compressées à la volée, ce qui peut consommer beaucoup de CPU.

Il existe une sécurité au niveau de IIS permettant de désactiver la compression GZIP en cas de forte charge, cela permet à IIS de répondre à plus de requête tout en étant dans un mode dégradé. Par défaut, pour la compression dynamique son fonctionnement est le suivant : si le pourcentage d’utilisation du CPU moyen sur 30 secondes est supérieur à 90, alors IIS désactivera la compression gzip ; si cette valeur moyenne redescend en dessous de 50, alors IIS réactivera la compression. Il existe le même mécanisme pour la compression statique, cette sécurité est désactivée par défaut.

Ces pourcentages se configurent grace aux attributs dynamicCompressionDisableCpuUsage et dynamicCompressionEnableCpuUsage pour la compression dynamiques et les attributs staticCompressionDisableCpuUsage et staticCompressionEnableCpuUsage pour la compression statique. Il est également possible de désactiver cette sécurité en spécifiant les attributs xxxCompressionDisableCpuUsage à 100.

<configuration>
  <system.WebServer>
    <httpCompression dynamicCompressionDisableCpuUsage="90" dynamicCompressionEnableCpuUsage="50" 
staticCompressionDisableCpuUsage="100" staticCompressionEnableCpuUsage="50"


doDiskSpaceLimiting="true"
>

D’autres paramètres existent et sont listés sur cette page : httpCompression Element [IIS 7 Settings Schema] [MSDN]

Personnellement, il m’arrive de modifier la valeur de dynamicCompressionEnableCpuUsage pour la mettre à 85. En effet, il est possible que certains serveur soit fortement sollicités et dépassent la moyenne de 90% puis reste autour de 60% d’utilisation.

Et vous, avez-vous déjà modifié ces paramètres ?

Include typé et Entity Framework

Par défaut, lorsque l’on fait une requête Entity Framework, les objets connexes ne sont pas chargés, il faut explicitement indiquer que l’on souhaite charger ces objets.

Il existe 2 solutions pour charger les objets connexes. Soit on active le lazy-loading, ainsi les objets seront chargés lors de l’accès à la propriété. Soit on indique à Entity Framework quelles sont les objets connexes à charger, cela se fait via la méthode Include de ObjectQuery. Cette méthode prend un paramètre de type String indiquant quelles sont les propriétés à charger en même temps que l’objet.

Ainsi, si l’on souhaite charger les clients en même temps qu’une commande, il faudra inclure la propriété Customer, le code ci-dessous illustre mon propos.

var q = entities.Orders.Include("Customer").Where(o => o.CustomerId == 3);

foreach(var order in q){ 
  // order.Customer ne sera ici pas null
}

Le problème de ce bout de code est que si un jour on change le nom de la propriété Customer et que l’on oublie de changer le code du Include, il n’y aura aucune erreur de compilation, le code plantera avec une exception de type “InvalidOperationException” lors de l’exécution :

System.InvalidOperationException: A specified Include path is not valid. The EntityType 'XXX.Data.Entities.Order' does not declare a navigation property with the name 'Cuuustomer'.

Afin de palier à ce problème, il est possible de créer sa propre méthode d’extension Include qui prend non pas une chaine de caractère mais une lambda expression permettant de spécifier la propriété que l’on veut inclure. Le code ci-dessus devient alors :

var q = entities.Orders.Include(o => o.Customer).Where(o => o.CustomerId == 3);

foreach(var order in q){ 
  // order.Customer ne sera ici pas null
}

Ainsi, le nom de la propriété Customer sera vérifié lors de la compilation, ce qui limite donc grandement les erreurs.

Si l’on veut inclure plus d’une profondeur d’objet, par exemple, si l’on souhaite inclure les clients d’une commande ainsi que ses adresses, Entity Framework propose de séparer les propriétés par des points. Le bout de code suivant fonctionnera :

var q = entities.Orders.Include("Customer.Addresses").Where(o => o.CustomerId == 3);

foreach(var order in q){ 
  // order.Customer.Addresses ne sera ici pas null
}

Dans ce cas, il est assez simple de faire une méthode d’extension pour ce cas particulier, on pourrait alors écrire ceci :

var q = entities.Orders.Include(o => o.Customer.Addresses).Where(o => o.CustomerId == 3);

foreach(var order in q){ 
  // order.Customer.Addresses ne sera ici pas null
}

Le problème se pose lorsque l’on veut inclure une propriété après une collection, par exemple lorsque l’on veut inclure les pays de toutes les adresses du client d’une commande. Dans ce cas, puisqu’on travaille sur une collection d’adresses, il n’est pas possible de retomber sur un élément pour accéder à une propriété précise. Une solution courante est d’utiliser la méthode First, lors de la conversion de l’expression en String, les appels à la méthode First ne seront pas pris en compte. Exemple :

var q = entities.Orders.Include(o => o.Customer.Addresses.First().Country).Where(o => o.CustomerId == 3);

foreach(var order in q){ 
  foreach(var address in order.Customer.Addresses){
      // address.Coutnry ne sera ici pas null 
  }
}

Une autre solution possible serait d’avoir plusieurs paramètre sur notre include et que chaque paramètre soit une lambda expression qui prend un entrée soit le type de l’argument précédent, soit le type de l’élément de la collection si l’élément précédent était une collection. Ainsi, on pourrait écrire notre include de cette façon :

var q = entities.Orders.Include(o => o.Customer, c => Addresses, a => a.Country).Where(o => o.CustomerId == 3);

foreach(var order in q){ 
  foreach(var address in order.Customer.Addresses){
      // address.Coutnry ne sera ici pas null 
  }
}

Avec cette solution, il existe une multitude de signature possible. Pour générer ces différentes signatures, j’ai utilisé un fichier T4.

Mon fichier T4 permet de générer des signatures avec une profondeur de 6, cela veut dire qu’il sera possible de mettre 6 paramètres à notre fonction Include. Une profondeur de 6 nous génère 64 signatures différents, au-delà, Visual Studio a beaucoup de mal à afficher l’intellisense … Cette limitation est acceptable, avoir des includes a plus de 6 niveaux de profondeur est déjà important, le SQL généré risque d’être déjà très complexe. De plus, lorsque l’on ne travaille pas sur une collection, on peut réunir les paramètres, l’include ci-dessus peut donc s’écrire ainsi :

var q = entities.Orders.Include(o => o.Customer.Addresses, a => a.Country).Where(o => o.CustomerId == 3);

foreach(var order in q){ 
  foreach(var address in order.Customer.Addresses){
      // address.Coutnry ne sera ici pas null 
  }
}

Le code de mon fichier T4 se trouve sur csharpfr : Entity Framework : Avoir un includé typé. Pour l’utiliser, il suffit d’inclure le fichier T4 et le fichier ExpressionExtensions.cs dans votre solution.

Pour ma part, je trouve cette solution assez élégante, le seul problème est le nombre de signature assez importante que cela génère. Et vous, qu’en pensez-vous ? Préférez-vous cette solution à la solution du .First() ? Avez-vous une autre solution ? Comment faites-vous vos includes ?

Comment obtenir les indicateurs de performances d’une requête SQL avec SQL Server ?

Lorsque l’on réalise une application qui utilise un serveur de base de donnée, il est toujours intéressant de regarder quelles sont les performances des requêtes SQL que notre application exécute.

Il y a plusieurs solutions pour arriver à ces fins.

La première, la plus connue, est d’utiliser SQL Server Profiler. Il s’agit d’un outil qui va analyser tout ce qui transite sur le serveur SQL.

Lorsque vous lancez l’outil, il va d’abord vous demander de vous connecter sur un serveur SQL, puis, il va vous demander un template. Un template est un ensemble de règle qui permet de tracer seulement certains événements. Je trouve que le template par défaut log trop de choses et qu’il manque certaines données importantes.

Pour ma part, je ne logge que ces informations :

image

J’ai rajouté la colonne “RowCounts” qui permet de savoir le nombre de ligne retourné par la requête, et supprimé les événements “RPC:Starting” et “SQL:BatchStarting” qui surgisse dès le début de la requête, avant même que SQL ait commencé l’analyse.

Ensuite, après avoir configuré la trace, vous allez voir apparaitre les différentes requêtes qui sont jouées sur votre serveur.

image

Les colonnes intéressantes sont les colonnes :

  • CPU : nombre d’unité de CPU consommé (unité arbitraire)
  • Reads : nombre de pages lues
  • Writes : nombre de pages écrites
  • Duration : durée d’éxécution en ms
  • RowCounts : nombre de ligne retourné par la requête

Il est également possible de mettre des flags au niveau de votre session SQL Server, ainsi pour chaque batch de votre session, vous aurez le détail des accès disques et durée d’exécution. Les flags à spécifier sont les flags suivants :

set statistics io on 
set statistics time on 

select * 
from magelia.Address 
where Name = 'home'

Au niveau des messages vous obtiendrez alors :

SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.
SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

(8142 row(s) affected)
Table 'Address'. Scan count 1, logical reads 2775, physical reads 0, read-ahead reads 0, lob logical reads 5496, lob physical reads 0, lob read-ahead reads 5242.

 SQL Server Execution Times:
   CPU time = 78 ms,  elapsed time = 778 ms.

 SQL Server Execution Times:
   CPU time = 78 ms,  elapsed time = 778 ms.

Ne vous focalisez pas sur le temps d’exécution ! Lorsque vous êtes tous seul sur votre base de données, le disque dur est peu sollicité, les durées d’exécution seront globalement bonnes. Par contre, focalisez-vous sur le nombre de pages lues : ce qui est couteux sur un serveur SQL est les accès disques. Il est donc préférable d’avoir une requête qui consomme très peu de pages lues plutôt qu’une requête un peu plus rapide qui consomme beaucoup plus de pages lues.

Et vous qu’utilisez-vous pour connaitre les performances des requêtes SQL que vous écrivez ?

Historique de configuration avec IIS7

A partir de IIS7, toute la configuration IIS se trouve dans un fichier de configuration XML. Ce fichier est situé dans le dossier C:\Windows\System32\inetsrv\config\applicationHost.config.

Il existe un mécanisme permettant de sauvegarder automatiquement ce fichier en cas de modification. Cela permet ainsi de revenir en arrière lorsque l’on a fait une mauvaise manipulation.

Par défaut, ce mécanisme est activé et va enregistrer l’historique des changements dans le dossier %SYSTEMDRIVE%\inetpub\history\.

image

Mais comment fonctionne ce mécanisme et comment modifier son comportement ?

Ce mécanisme repose sur le service Windows “Application Host Helper Service”.

image

Il existe 4 paramètres permettant de modifier cette fonctionnalité :

Nom valeur par défaut Description
enabled True Indique si l’historisation est activé
path %systemdrive%\inetpub\history Chemin vers lequel les sauvegardes seront stockées
maxHistories 10 Nombre maximum de sauvegarde à conserver.
period 00:02:00 interval de temps entre lequel le service Windows va vérifier les modifications. Si une modification a été réalisée depuis la dernière vérification, le service va sauvegarder la configuration.

Il s’agit des valeurs par défaut, si vous voulez les modifier, cela se passe dans le fichier applicationHost.config. La section system.applicationHost accepte une section configHistory, par défaut cette section n’est pas présente, il faut la rajouter.

<system.applicationHost>
  <configHistory maxHistories="15" period="00:00:10" path="%systemdrive%\MyWebHistory" enabled="True" />

Et comment restaurer ces backups ?

Il est possible de manipuler ces sauvegardes depuis la ligne de commande appcmd. Les différentes commandes sont :

  • appcmd list backup
      
    Permet de lister les sauvegardes présentes localement
  • appcmd add backup [{BackupName}]
       Permet de créer un nouveau backup
  • appcmd delete backup {BackupName}
       Permet de supprimer un backup
  • appcmd restore backup {BackupName}
      
    Permet de restaurer un backup

Et vous, avez-vous déjà utilisé ce système de sauvegarde. Vous a-t-il déjà sauvé ?

IIS – Comment connaitre le serveur ayant répondu à la requête lors de l’utilisation d’un Load-Balancer

Il est de plus en plus fréquent qu’un site web utilise plus d’un serveur Web pour fonctionner. Cela se fait généralement grâce à un load-balancer, un équipement qui se met devant tous les serveurs web et qui va distribuer les requêtes vers les différents serveurs.

Parfois, il est intéressant de savoir quel serveur nous a répondu, c’est surtout utile en debug quand juste un serveur ne fonctionne pas correctement.

Pour ce faire, la solution la plus simple est d’utiliser les en-têtes HTTP. Un en-tête HTTP est une ligne que peut ajouter le serveur web au tout début de la réponse, les en-têtes sont couramment utilisés pour définir le cache client, les cookies, etc.

Au niveau de IIS, il est possible de définir des entêtes HTTP soit au niveau d’un site web, soit au niveau du serveur. Je vous conseille de rajouter cet en-tête au niveau du serveur, ainsi tous les sites en profiteront.

Pour cela, lancez la console d'administration IIS, au niveau du serveur, chercher la fonctionnalité “HTTP Response Header

image

Puis ajouter une entrée :

image

Le nom de l’entête n’a pas d’importance, il ne s’agit que d’une information qui sera utilisé lors du debug. Cependant, certains nom sont réservés, la liste de ces en-têtes sont définies ici : IANA Permanent Message Header Field Names et IANA Provisional Message Header Field Names. De plus, éviter de mettre des informations confidentielles.

La plupart des outils de debug web permettent de lires les en-têtes HTTP : Fiddler, Firebug, Internet Explorer, etc. La capture ci-dessous montre les entêtes HTTP avec Internet Explorer.

image

Pour en savoir plus sur les en-têtes HTTP : List of HTTP header fields [Wikipedia] 

Et vous, utilisez-vous couramment cette technique ?

A quoi correspond le mot clé let d’une expression Linq ?

Depuis .net 3.5, il est possible d’écrire des expressions Linq. Une expression Linq correspond à une expression utilisant les mots clé from, where, select, etc.

Le bout de code ci-dessous est une expression Linq.

int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
 
var lowNums =
    from num in numbers
    where num < 5
    select num + 1;

Une expression Linq n’est qu’un sucre syntaxique : après compilation il n’y a plus de trace de ces expressions, elles sont transformés1 en code C# “classique”. Le code ci-dessus est donc strictement équivalent au code ci-dessous :

var lowNums = numbers
                .Where(num => num < 5)
                .Select(num => num + 1); 

Pour en savoir plus sur Linq : Linq (Language Integrated Query) [MSDN]

Personnellement, je n’aime pas la syntaxe Linq. Je trouve que cette syntaxe est limitée, dès que l’on a besoin de faire des choses un peu complexe, on retourne sur une syntaxe “classique”. 

Récemment, je me suis retrouvé en face d’une expression linq contenant le mot clé let. Ce mot clé permet de stocker temporairement le résultat d’une sous-expression.

Par exemple :

String sentence =  "A penny saved is a penny earned.";
 
// Split the sentence into an array of words and select those whose first letter is a vowel.
var earlyBirdQuery =
    from word in sentence.Split(' ')
    let w = word.ToLower()
    where w[0] == 'a' || w[0] == 'e' || w[0] == 'i' || w[0] == 'o' || w[0] == 'u'
    select word;

Le code ci-dessus permet d’extraire tous les mots commençant par une voyelle d’une phrase. Le mot clé let permet ici de stocker le mot converti en minuscule.

Je me suis donc demandé comment était traduit ce mot clé. Si l’on regarde le code généré avec ILSpy on obtient le code suivant :

var earlyBirdQuery = 
    sentence.Split(' ')
            .Select(word => new { Word = word, loweredWord = word.ToLower() })
            .Where(o => o.loweredWord[0] == 'a' || o.loweredWord[0] == 'e' || o.loweredWord[0] == 'i' || o.loweredWord[0] == 'o' || o.loweredWord[0] == 'u')
            .Select(o => o.Word); 

Le mot clé let est donc traduit par une projection avec la création d’un type anonyme, il n’y a donc rien de magique.

Et vous, utilisez-vous les horribles mots clé Linq ?


1 : C’est, en fait un peu plus complexe car le code C# “classique” utilise les méthodes d’extension qui sont eux aussi un sucre syntaxique. De plus, si l’expression Linq se fait sur un IQueryable, alors C# va générer le code permettant d’avoir une instance d’une Expression.
Gestion des contenus localisés avec Entity Framework – Optimisation de requêtes

Il est fréquent d’avoir du contenu localisé dans une base de données. J’ai récemment eu besoin d’extraire de telles données avec Entity Framework, j’ai alors cherché une solution permettant d’avoir de bonnes performances.

Il existe plusieurs possibilités de modélisation de contenu localisé. Pour ma part, lorsque je dois stocker un objet localisable, je choisi de créer 2 tables : une table contenant les données non localisées et une table contenant les données localisées.

L’identifiant de langue que j’utilise correspond à la propriété LCID d’une culture .net. Pour rappel, une culture .net est constituée d’une langue et, éventuellement, d’une région. Il est ainsi possible d’avoir une culture française neutre, une culture française pour la France (fr-FR), etc. Vous trouverez ci-dessous un tableau listant les cultures que je vais utiliser pour ce post.

image

Ainsi, si je dois stocker des produits, j’aurais la modélisation suivante :

image_thumb1_thumb

Je souhaite utiliser Entity Framework pour récupérer des produits avec le contenu localisé dans une culture donnée. Si le produit n’est pas localisé dans la culture demandée, je souhaite récupérer le contenu dans la culture parente ou la culture neutre.

Par exemple : soit un produit stocké dans la base avec les données localisées pour les cultures “fr”, “en-US” et “” (invariant). Si je demande ce produit en “fr-FR”, je souhaite obtenir le contenu “fr”. Si le produit est localisé dans aucune langue, je souhaite retourner null.

Comme ce genre de requête sera utilisé très fréquemment, j’ai cherché à optimiser les performances. Je me suis principalement concentré sur le nombre de pages lue. En effet, sur ma machine de développement, je n’ai pas de charge, le disque dur est donc peu sollicité, le temps d’exécution de la requête sera donc excellent. Le nombre de pages lues reste constant en fonction de la charge, c’est ce nombre qu’il faut réduire au maximum pour avoir une application qui a de bonnes performances en cas de forte charge.

Afin de faire mes tests, j’ai chargé la base avec un grand nombre de donnée, cela permettra de bien mettre en avant les problèmes IO. Les tables Product et ProductLocalized contiennent respectivement 1.2 et 6.3 millions de ligne.

Afin de ne pas travailler sur les 1.2 millions de ligne, je filtre ma requête par TypeId. Un TypeId est associé à environ 100 produits. Pour optimiser les requêtes, j’ai ajouté l’index suivant :

CREATE NONCLUSTERED INDEX [IX_Product_TypeId] ON [dbo].[Product] 
(
    [TypeId] ASC
)
INCLUDE ([ProductId], [SKU]) 

J’utilise SQL Server édition développeur et EF 4.0, chacun de mes tests sont joués plusieurs fois, le plan d’exécution est donc en cache. Les résultats affichés sont une moyenne des dernières exécutions. Les données proviennent de SQL Profiler.

La première requête que j’ai écrite a été la suivante :

image_thumb8_thumb

Les performances de cette requête sont les suivantes :

image

J’ai ensuite modifié la requête pour factoriser les différents IF, ma requête est devenue la suivante :

image_thumb11_thumb

Les résultats sont les suivants :

image

Cette requête est moitié moins couteuse que la première. Pour ce cas, Entity Framework ne mutualise pas les bouts d’expression identiques, il est donc plus optimisé de faire une pré-requête qui permettra de préselectionner les données.

La requête SQL et plan d’exécution correspondants sont les suivants :

image

image_thumb25_thumb

Il y a 6 Index Seek dont 5 clustered . Le besoin est seulement de récupérer des données provenant de 2 tables, il doit être possible d’obtenir 2 Index Seek.

J’ai ensuite essayé la requête EF suivante :

image_thumb27_thumb

La requête générée et le plan d’exécution sont les suivants :

image

image_thumb30_thumb

Les performances sont les suivantes :

image

Le plan d’exécution n’a que 2 Index Seek, cependant les performances sont bien moins bonnes que la requête précédente. Le cout principal de la requête est un sort. Ce sort est fait sur un “case when”, il n’est donc pas possible d’ajouter un index pour optimiser la requête.

Je suis donc parti sur une toute autre piste en utilisant une jointure entre les cultures souhaitées et les enregistrements demandés. J’ai alors obtenu la requête suivante :

image_thumb17_thumb

La syntaxe est un peu étrange, on fait une jointure entre un tableau d’objet anonyme en mémoire et la table ProductLocalized.

La requête SQL et le plan d'exécution correspondants sont les suivants :

image

image_thumb22_thumb

Les résultats :

image

Le plan d’exécution parait complexe, cependant il n’y a 2 Index Seek, toutes les autres opérations sont des petites opérations sur des constantes ou autres. Les performances sont 10 fois meilleures que la première requête. J’ai essayé de faire la jointure dans l’autre sens, il n’y a pas eu de différence.

On voit donc qu’il est important de tester et analyser les différentes requêtes générée par Entity Framework. Il est souvent possible d’obtenir un même résultat en écrivant la requête de façon différente, l’analyse de la requête et de son plan d’exécution permet parfois des grands gains de performance. Par rapport à ma requête initiale, je suis passé de de 5000 pages lues à 480, soit un gain supérieur à 1000%.

Pour ma part, je ne pense pas qu’il soit possible d’optimiser davantage cette requête. Avez-vous une autre idée pour optimiser encore plus cette requête ? Que ce soit en SQL ou avec Entity Framework ?

.net, l’infini et SQL Server

J’ai récemment eu besoin de représenter l’infini dans une base SQL Server. J’ai alors fait quelques recherches et j’ai découvert les constantes PositiveInfinity et NegativeInfinity au niveau des types Single et Double (respectivement float et double).

Ces constantes sont décrites par le standard IEEE 754 (Wikipedia). Il ne s’agit donc pas d’un concept propre à .net mais d’un concept propre à l’informatique en général.

J’ai testé plusieurs opérations avec l’infini, les règles mathématiques semblent respectées. Plusieurs choses sont cependant à noter :

  • Il existe une constante NaN (Not a Number) qui correspond à un nombre qui ne représente pas un nombre.
  • L’infini est égale à elle même
  • NaN n’est pas égale à lui-même (comme en JavaScript)

J’ai alors essayé d’insérer ces constantes dans SQL Server, ADO.net déclenche une exception. Ces constantes ne sont pas définis au niveau de SQL Server !

J’ai cherché sur le net, et ce comportement est apparu à partir de SQL Server 2005, un bug connect a été créé à ce propos : Storing IEEE 754 Floats in SQL Server 2005 (NaN, +/- Infinity)

J’ai fait d’autres tests avec ces constantes. Si l’on sérialise cette valeur en SOAP, la valeur est bien transmise, la spécification SOAP définit les types float et double comme le décrit le standard IEEE 754. Si l’on sérialise en JSon via le JavaScriptSerializer, la valeur est bien transmise, JavaScript définit également ces constantes.

Si l’on souhaite vraiment stocker l’infini dans une base SQL Server, je vois alors plusieurs possibilités.

  • soit on utilise une colonne de type binary(32) et on doit refaire tous les calculs ;
  • soit on utilise une colonne XML et les performances seront dégradées ;
  • soit on rajoute une colonne de type smallint et on traite les cas particuliers (number, NaN, NegativeInfinity, PositiveInfinity). Il s’agit de la solution que j’ai utilisé ;
  • soit on utilise un UDT .net de type float ou double (merci à Christian pour l’astuce).

Et vous, avez-vous eu besoin de stocker l’infini dans une base de donnée ?

A t-on encore besoin de développeurs ?

Depuis quelques temps, on peut lire des réflexions sur le métier de développeur par rapport au métier de chef de projet : ici, ou ou encore ici et encore et surement ailleurs. Globalement, on se rend compte que de plus en plus de personnes se plaignent des développeurs qui ne veulent pas développer mais qui veulent évoluer : la consécration, devenir chef de projet.

Après avoir lu ces billets, je suis surpris de voir qu’une question ne semble pas avoir encore été abordée et qui me semble pourtant fondamentale : A quoi sert un développeur ? A t-on encore besoin de développeur ?

La question peut surprendre mais peut apporter certains éléments de réponse. Si je regarde les différentes applications sur lesquelles j’ai eu l’occasion de travailler ou sur les applications qui sont autour de moi, je me rends compte que l’on a de moins en moins besoin de développeurs mais plutôt d’intégrateurs.

Je m’explique.

De nos jours, la majorité des développements reposent sur un ou plusieurs frameworks. Cela permet d’être plus productif et évite de recoder la roue. Globalement, le client dispose du logiciel plus rapidement et pour moins cher. Certes, le logiciel ne sera pas parfait, mais il répondra plus ou moins aux attentes de l’utilisateur.

Ces frameworks deviennent de plus en plus complets : un développeur a de moins en moins besoin d’être technique pour réaliser une application. On n’a plus besoin de développeurs qui savent écrire des algorithmes compliqués. Qui a déjà écrit un algorithme de tri sur une application récente ?

Dans sa globalité, je ne pense pas que ce soit une mauvaise chose. A part les développeurs geek, les amoureux du code, tout le monde est globalement content. Bien sûr, les geeks que nous sommes se sentent frustrés. Intégrer un framework n’est pas quelque chose de passionnant, tous le code intéressant est déjà écrit dans le framework …

Vous allez me dire que oui : il reste toujours à écrire les frameworks. Certes. Mais à votre avis quelle est la proportion de développeurs qui écrivent des frameworks par rapport aux autres ? 10% ? 5% ? 1% ? Quand bien même, combien d’entre vous utilisent des frameworks écrit en France ?

Il restera toujours le poste d’expert/architecte très bien décrit par Jérémy Jeanson. Ce poste est le pompier, c’est lui qui connaitra parfaitement le framework, c’est lui qui débloquera les développeurs intégrateurs en cas de problème, c’est à lui que sera confié le développement de bout de code complexe, d’algorithmique si toutefois aucun framework ne l’a pas déjà fait. Mais ce poste est rare et ne représentera qu’un faible pourcentage des développeurs intégrateurs.

 

Je connais principalement les outils Microsoft.

Si l’on regarde l’évolution des framework Microsoft, on aperçoit leur trajectoire : d’ici quelques années, toutes nos applications risquent d’être dévelopées en drag & drop. Cela a déjà commencé. Regardez ASP.net d’il y a 5 ans par rapport à maintenant ! Regardez ASP.net Dynamic Data qui date de quelques années déjà ! Regardez LightSwitch qui vient de sortir.

Lorsque l’on regarde la roadmap de Visual Studio, le principal outil de Microsoft, on voit autre chose : Visual Studio va se rapprocher des décideurs, des gens du métier. Visual Studio/TFS ne sera plus seulement l’outil du développeur mais de plus en plus l’outil de toutes les personnes liées au cycle de vie de l’application.

image

Notre métier n’en est qu’au commencement, il est en pleine phase de transition. 

La façon dont je vois les choses et que notre métier va se décomposer en plusieurs groupes :

  • les intégrateurs
  • Les développeurs de framework

Tous les domaines ne sont pas au même niveau d’avancement, les applications web semblent clairement en avance par rapport à d’autres domaines tel que l’embarqué ou le temps réel mais ce n’est que le commencement.

Je ne crains pas ce changement, je ne pense pas que ce soit une mauvaise chose. Les intégrateurs vont de plus en plus se focaliser sur la complexité métier de leur application, ils vont de plus en plus se concentrer sur les réels besoins du client, les applications auront donc la chance d’être de plus en plus pertinente, plus utile.

Je ne critique pas les développeurs actuels, je n’ai rien non plus contre les intégrateurs futurs ou actuels. Oui ! Je déplore cette situation mais je reste un geek après tout ;-)
Attention, avant que l’on me fasse les mêmes remarque qu’a Julien, oui, malgré avoir travaillé en SSII, grand compte, freelance, éditeur, je n’ai peut-être pas suffisamment de recul sur la question mais c’est mon avis.

Du coup, avec cette vision, comment pensez-vous qu’une application se réalisera d’ici 10 ans ? 20 ans ? 50 ans ? Aurons-nous toujours besoin de développeurs ? Ne sera-t-il pas qu’un intégrateur ? un drag & droppeur ?

Lancer un build pour une version spécifique du code source : label, changeset, etc.

Team Foundation Server dispose d’un serveur de build. Il s’agit d’un serveur qui va exécuter un processus complexe : compilation, ajout de label, analyse de code, constitution de package de déploiement, etc.

Par défaut, lorsque l’on lance un build, le serveur va récupérer la dernière version du code source.

image

Dans la majorité des cas, c’est ce que l’on souhaite. Cependant, si vous avez personnalisé le workflow d’un build, il se peut que vous souhaitiez relancer un build spécifique afin de réaliser une action que seul le serveur de build peut faire ou que le serveur de build réalise de façon automatisé, par exemple un déploiement d’application.

Dans ce cas, dans l’onglet Parameters, il est possible d’indiquer un numéro de version spécifique. Le format de ce numéro de version est le suivant :

  • pour un changeset : C + N° du changeset (exemple : C48840)
  • pour un label : L + N° du label (exemple : L1.4.346.2)
  • pour une date : D + date au format du serveur TFS (exemple : D08/11/2011)
  • pour la dernière version : T
  • pour la version d’un workspace :  W + workspaceName + “;” + owner (exemple : W:pouetComputer;cdurand)

image

Plus d’information sur le format de version ici : Command Line Syntax (partie Versionspecs)

Et vous, aviez vous déjà utilisé ces fonctionnalités ?

.net 4 et Lazy-loading thread-safe : un nouveau type bien pratique

.net 4.0 a introduit plein de petites nouveautés au sein de mscorlib : l’assembly principale du framework .net.

L’une des nouveautés que j’aime bien et le type Lazy. Ce type permet de faire du lazy-loading simplement et permet surtout de le faire de façon thread-safe.

Tout d’abord, revenons sur le principe de lazy-loading. Il s’agit d’un design pattern permettant le chargement ou la création de la valeur d’une propriété à la demande.

Imaginons que vous disposez d’une classe Pouet qui comporte plusieurs propriétés dont la propriété HeavyProperty. Comme son nom l’indique, cette propriété est très couteuse, on va donc vouloir créer la valeur seulement lorsque l’on en a vraiment besoin.

Le code ci-dessous est un exemple d’une propriété en lazy-loading :

public class Pouet
{
    private HeavyObject _heavyProperty;
    public HeavyObject HeavyProperty {
        get {
            if (this._heavyProperty != null) {
                this._heavyProperty = new HeavyObject();
            }
            return this._heavyProperty;
        }
    }
}

Bien que fonctionnel, ce code n’est pas parfait : il n’est pas thread safe. Si 2 threads accèdent simultanément à la propriété, alors le constructeur de HeavyObject peut être appelé deux fois.

Souvent, ce n’est pas gênant. Si l’instance de Pouet est utilisé par un seul thread, il est alors inutile de les rendre thread-safe. De plus, si la valeur de la propriété n’a pas besoin d’être unique et si l’initialisation de l’objet n’est pas très couteux, inutile de sortir l’artillerie lourde. Dans les autres cas, ce comportement peut poser bien des soucis.

Avec .net 3.5, on pouvait utiliser le mot clé lock ou la classe Monitor. Personnellement, je préfère utiliser la classe Monitor, cela permet de définir un timeout et ainsi éviter les deadlock. En interne, le mot clé lock utilise également la classe Monitor.

Au final, une propriété en lazy-loading thread-safe s’écrit comme ca en .net 3.5 :

public class Pouet
{
    private Object _heavyPropertyLock = new Object(); 
    private HeavyObject _heavyProperty;
    public HeavyObject HeavyProperty {
        get {
            if (this._heavyProperty == null) {
                if (!Monitor.TryEnter(this._heavyPropertyLock, 4000)) {
                    throw new Exception("can't acquire lock"); 
                }
                try {
                    // double check. Check if another thread didn't create the HeavyObject 
                    // while this thread was waiting for aquiring the lock object
                    if (this._heavyProperty == null) {
                        this._heavyProperty = new HeavyObject();
                    }
                }
                finally {
                    Monitor.Exit(this._heavyPropertyLock); 
                }
            }
            return this._heavyProperty;
        }
    }
}

Comme on peut le voir, cela devient relativement lourd.

Avec .net 4.0, il existe le type Lazy qui va permettre de simplifier tout ça. Voici le même code que ci-dessus avec le type Lazy :

public class Pouet
{
    private Lazy<HeavyObject> _heavyProperty = new Lazy<HeavyObject>(() => new HeavyObject());
    public HeavyObject HeavyProperty {
        get {
            return this._heavyProperty.Value;
        }
    }
}

Plutot simple non ?

Cela fonctionne ainsi. Dans l’exemple ci-dessus, le constructeur du type Lazy prend un Func<T>. Ce Func<T> doit retourner une instance de l’objet que l’on veut initialiser. Lors du premier accès à la propriété Value, le type Lazy va executer le Func<T> tout en faisant en s’occupant de la synchronisation des threads afin d’éviter que 2 threads initialisent simultanément l’objet.

A noter qu’il existe plusieurs constructeurs. Il existe un constructeur vide, dans ce cas, le type Lazy va initialiser le type T à l’aide de son constructeur par défaut.

Un autre constructeur prend en entrée un LazyThreadSafetyMode.

image

Ce paramètre permet d’indiquer la façon dont le type Lazy doit gérer la concurrence de thread. Il s’agit d’une enum qui a 3 valeurs :

  • None                              : le type Lazy ne garantit pas que l’initialisation soit effectuée qu’une seule fois
  • PublicationOnly                : Plusieurs threads peuvent initialiser l’objet. Le premier thread qui termine l’initialisation définit la propriété Value, tous les threads utiliseront cette valeur.
  • ExecutionAndPublication   : Seul un thread peut initialiser l’objet. Valeur par défaut.

Connaissiez-vous le type Lazy<T> ? L’aviez-vous déjà utilisé ?

C# – Mais comment prononcer une lambda expression ?

Mais, comment expliquer oralement un bout de code contenant une lambda expression ?

Prenons un exemple, comment dites-vous les lignes de code suivantes ?

IEnumerable<String> values = new String[] { "a", "ab", "bc", "cd" };
values = values.Where(value => value.StartsWith("a")); 

La lambda expression correspond à “value => value.StartsWith("a")”. 

Actuellement, je lis la 2ème ligne du premier bout code ainsi : “values égale  values point where value   lambda   value point StartsWith a”. (Je sépare les temps de pause par plusieurs espaces)

S’il y a plusieurs arguments et des accolades, par exemple :

values = values.Where((value, i) =>
    {
        if (value.StartsWith("a"))
            return true; 
        else 
            return false; 
    });

Alors je lis “values  égale  values point where value virgule i   lambda   entre accolade if value point startswith a   return true   else   return false”

Autant, pour les personnes qui connaissent bien le mécanisme sous-jacent, cela reste compréhensible. Pour les débutants qui ne connaissent pas les lambda expressions, la façon dont on lit ce code n’est pas explicite. J’ai donc cherché d’autres possibilités.

Tout d’abord, revoyons à quoi correspond une lambda expression. Le bout de code ci-dessus est équivalent au code suivant :

IEnumerable<String> values = new String[] { "a", "ab", "bc", "cd" };
Func<String, Boolean> fnc = new Func<String, Boolean>(delegate(String value) { return value.StartsWith("a"); }); 
values = values.Where(fnc);

Qui lui-même est équivalent au code suivant :

public static void Main(String[] args)
{
    IEnumerable<String> values = new String[] { "a", "ab", "bc", "cd" };
Func<String, Boolean> fnc = new Func<String, Boolean>(StartsWithA); values = values.Where(fnc); } private static Boolean StartsWithA(String value) { return value.StartsWith("a"); }

Une lambda expression est donc un sucre syntaxique : cela permet de simplifier l’écriture du code. Une lambda expression est un moyen d’envoyer du code à une autre méthode. Malgré ce retour aux origines, cela ne m’a pas permis de trouver une autre façon de lire une lambda expression.

J’ai donc cherché sur le net les réflexions d’autres personnes. J’ai trouvé plusieurs liens traitant de ce sujet. Voici comment certains lisent le => d’une lambda expression:

Après réflexion, tout dépend du contexte.

Pour les personnes connaissant bien le mécanisme des lambda expressions, je vais lire => “lambda”.

Lorsque je parlerais à un débutant, je vais utiliser des verbes décrivant l’action. Parmi ces verbes, “devient” pour une projection, “tel que” pour une condition ou encore “conduit à” dans les autres cas.

Pour les débutants ne connaissant pas du tout les lambda expressions, je vais le lire ainsi “point where lambda avec en entrée [String] value   [int] i   et [int] j    [entre accolade] value point StartsWith en fonction". J’ai mis la prononciation des types entre crochet, je les prononcerais en fonction de la facilité avec laquelle la personne pour qui l’on explique le code sera capable d’inférer les types.

Et vous ? Comment prononcez-vous une lambda expression ?

Entity Framework - mettre à jour la documentation à partir de la documentation des champs de la base de données

Avec SQL Server, il est possible d’ajouter des commentaires sur les tables et sur les colonnes.

Il y a plusieurs solutions pour ajouter ces informations :

  • soit depuis SQL Server Manager Studio :
    • en mode design sur les tables
      image
    • avec le diagramme de la base de donnée
      image
  • Soit en T-SQL

    EXECUTE sp_addextendedproperty --or sp_updateextendedproperty
              N'MS_Description', 'my description', 
              N'USER', N'dbo', 
              N'TABLE', N'MyTableName',
              N'COLUMN', N'MyColumnName' 
  • Pour récuperer la description, il faut ensuite executer la procédure suivante :

    SELECT [value] 
    FROM fn_listextendedproperty (
        'MS_Description', 
        'schema', 'dbo', 
        'table',  'MyTableName', 
        'column', 'MyColumnName')

Au niveau de Entity Framework, il est possible d’ajouter de la documentation au niveau des propriétés des entités.

image

Lorsque l’on renseigne le champ “Summary” de la documentation, les commentaires XML sont rajoutés, on voit alors la documentation depuis l’Intellisense des propriétés.

image

Malheureusement, lorsque l’on met à jour un fichier edmx à partir de la base de données, les descriptions ne sont pas mises à jour.

Je me suis intéressé au problème et fait quelques lignes de code pour synchroniser la documentation présente dans la base vers l’edmx.

J’ai mis mon projet sur codeplex : http://eftsqldocgenerator.codeplex.com/

 

image

Que pensez vous de cet outil ? Souhaitez-vous qu’il évolue (synchronisation bidirectionnelle, documentation sur les relations, …) ?

Comparaison de performance des sérialiseur XML .net (DataContractSerializer, XmlSerializer, SoapFormatter)

Il est fréquent que l’on veuille transformer un objet .net dans un format XML afin de le sauvegarder sur le disque dur, en base, etc. Cette opération correspond à la sérialisation XML.

Un sérialiseur est une classe .net permettant de faire cette opération de sérialisation. A partir de .net 3.5 SP1, .net fourni 3 sérialiseurs différents : XmlSerializer, SoapFormatter, DataContractSerializer.

Ces 3 sérialiseurs ont chacun leurs avantages et inconvénients. J’ai décidé aujourd’hui de m’intéresser aux performances de ces sérialiseurs. Les performances que je mesure ici sont le temps d’exécution, je ne mesure pas la consommation mémoire, l’éventuelle parallélisassion, la taille du fichier générée, etc. De plus, je ne prends pas en compte les spécificités de chacun des sérialiseurs, je me situe dans un cas d’utilisation très basique.

Environnement de test

Les tests ont été exécutés sur mon laptop (Win 7 enterprise, 8Go de ram, 8 coeur), en mode de compilation release, any CPU avec .net 4.0. Les temps de réponses affichés sont exprimés en millisecondes et correspondent à une moyenne d’au moins 3 exécutions.

J’ai également ajouté une comparaison en utilisant le BinaryFormatter, le sérialiseur qui transforme les données au format binaire.

J’ai effectué mes tests avec 3 types différents :

Type simple :

[Serializable] // required for soapformatter
public class A
{
    public int MyProperty { get; set; }
    public int MyProperty2 { get; set; }
}

Type intermediaire :

[Serializable] // required for soapformatter
public class B
{
    public int MyProperty { get; set; }
    public int MyProperty2 { get; set; }
    public String MyProperty3 { get; set; }
    public DateTime MyProperty4 { get; set; }
    public A MyPropertyA1 { get; set; }
    public A MyPropertyA2 { get; set; }
}

Type complexe :

[Serializable] // required for soapformatter
public class C
{
    public int MyProperty { get; set; }
    public int MyProperty2 { get; set; }
    public int MyProperty3 { get; set; }

    public A MyPropertyA1 { get; set; }
    public A MyPropertyA2 { get; set; }

    public B MyPropertyB1 { get; set; }
    public B MyPropertyB2 { get; set; }
}

A chaque sérialisation, je créé une nouvelle instance de ces objets, le code utilisé est le suivant :

static A GetA()
{
    return new A()
    {
        MyProperty = 1,
        MyProperty2 = 2
    };
}
static B GetB()
{
    return new B()
    {
        MyProperty = 1,
        MyProperty2 = 2,
        MyProperty3 = "3",
        MyProperty4 = DateTime.Now, 
        MyPropertyA1 = GetA(),
        MyPropertyA2 = GetA()
    };
}
static C GetC()
{
    return new C()
    {
        MyProperty = 1,
        MyProperty2 = 2,
        MyProperty3 = 3,
        MyPropertyA1 = GetA(),
        MyPropertyA2 = GetA(),
        MyPropertyB1 = GetB(),
        MyPropertyB2 = GetB()
    };
}

Enfin, le code utilisé pour l’exécution des tests est le suivant :

Serialisation :

Stopwatch watcher = Stopwatch.StartNew();

// SoapFormatter serializer = new SoapFormatter();
for (int i = 0; i < 10000; i++)
{
    SoapFormatter serializer = new SoapFormatter();
    using (MemoryStream ms = new MemoryStream())
    {
        B b = GetB(); 
        serializer.Serialize(ms, b);
    }
    if (i == 0)
    {
        Console.WriteLine(watcher.ElapsedMilliseconds);
    }
}
Console.WriteLine(watcher.ElapsedMilliseconds);

Déserialisation :

using (MemoryStream ms = new MemoryStream())
{
    var o = GetA();
    var innerSerializer = new DataContractSerializer(typeof(A));
    innerSerializer.WriteObject(ms, o);


    while (true)
    {
        Stopwatch watcher = Stopwatch.StartNew();

        var serializer = new DataContractSerializer(typeof(A));
        for (int i = 0; i < 10000; i++)
        {
            //var serializer = new DataContractSerializer(typeof(A));
            using (MemoryStream ms2 = new MemoryStream())
            {
                ms.Position = 0; 
                ms.CopyTo(ms2);
                ms2.Position = 0; 
                serializer.ReadObject(ms2);
            }
            if (i == 0)
            {
                Console.WriteLine(watcher.ElapsedMilliseconds);
            }
        }
        Console.WriteLine(watcher.ElapsedMilliseconds);
        Console.ReadLine();
    }
}

Les tests ont été réalisés selon deux modes, soit en instanciant à chaque fois un sérialiseur, soit en mutualisant le sérialiseur pour les 10 000 itérations.

Je différencie également le temps d’exécution de la première sérialisation des 9 999 suivants.

Résultat de test

Les résultats ci-dessous sont exprimés en millisecondes.

Sérialisation

Résultats lorsque l’on instancie un nouveau sérialiseur à chaque itération :

image

Résultat lorsque l’on mutualise l’instance du sérialiseur pour les 10 000 itérations :

image

Constat :

La première chose que l’on peut remarquer est qu’à part pour le XmlSerializer, il y a peu de différence entre la réutilisation ou non du sérialiseur. Dans le cas du XmlSerializer, on voit que réutiliser le type permet de meilleure performance. A noter que le XmlSerializer est Thread Safe il peut donc être stockée dans une variable static et mutualiser entre différents threads.

On voit ensuite que le XmlSerializer est le plus long pour des objets simples, dès que les objets deviennent plus complexes, le SoapFormatter devient de plus en plus lent.

Les sérialiseurs les plus rapides sont les BinaryFormatter et DataContractSerializer. Le BinaryFormatter est rapide dès la première exécution alors que le DataContractSerializer est un peu plus lent au démarrage, il devient ensuite plus rapide lorsque l’on effectue plusieurs sérialisations.

Déserialisation

Résultat lorsque l’on instancie un nouveau sérialiseur à chaque itération :

image

Résultat lorsque l’on mutualise l’instance du sérialiseur pour les 10 000 itérations :

image

Constat :

Comme lors de la sérialisation, on constate que l’initialisation du XmlSerializer est assez couteux, une fois de plus il est préférable de mutualiser l’instance du XmlSerializer.

Pour les types complexes, à part avec le SoapFormatter, la déserialisation prend à peu près le même temps. Pour les types simples, le BinaryFormatter reste le plus rapide.

Conclusion

Ces résultats nous amène aux conclusions suivantes :

  • Pour la déserialisation, le BinaryFormatter est le plus rapide suivi de très près par la DataContractSerializer
  • Pour la sérialisation,
    • pour des types complexe, le DataContractSerializer est le plus rapide
    • pour des types simple, le BinaryFormatter est le plus rapide
  • Si l’on utilise le XmlSerializer, il est préférable de conserver l’instance dans une variable static.
  • Le SoapFormatter est le serialiseur le plus lent

Attention, il ne faut pas oublier que chaque sérialiseur a ses propres spécificités. Lors du choix d’un sérialiseur, la performance n’est pas le seul critère à prendre en compte. De plus, je ne mesure pas le temps d’exécution lorsque l’on utilise ces spécificités.

Et vous, avez-vous constaté des différences de performance entre les différents sérialiseur ?

Fuite mémoire sur serveur de build avec TFS 2010 non SP1

Lorsque l’on met en place un serveur de build avec TFS, on arrive vite à avoir plusieurs dizaines de builds par jour.

Je me suis récemment rendu dans une entreprise où un serveur de build TFS 2010 a été mis en place. Lors d’une discussion avec un membre de l’équipe, on m’a remonté un problème avec ce serveur. Après plusieurs jours d’utilisation, le serveur devenait inutilisable, il fallait alors rebooter : environ 1 fois par semaine.

Ce souci est un problème connu, il existe une fuite mémoire sur le serveur de build TFS 2010. Ce problème a été corrigé avec le SP1 de TFS 2010.

Plus d’informations ici : Memory leaks and performance drop on TFS 2010 Build Service under heavy load et le KB se trouve ici : Microsoft Team Foundation Server 2010 Service Pack 1.

Si toutefois vous ne pouvez pas installer le SP1, il existe une solution de contournement : relancer le service de build quotidiennement.

Tout d’abord, il faut créer un fichier bat permettant de relancer le service. Celui-ci doit contenir ces lignes :

net stop TFSBuildServiceHost
net start TFSBuildServiceHost

Ensuite, il faut créer une tâche planifiée Windows exécutant quotidiennement le script.

image

Attention, n’oubliez pas de cocher l’option “Run wether user is logged on or not” et la case “Run with highest privileges”.

Bien sûr, ce n’est pas idéal. Si un build est en cours, celui-ci va échouer. Il faut donc planifier la tâche lorsqu’aucune activité n’a lieu.

Il s’agit d’une solution que j’ai mise en place avant la disponibilité du SP1.

Et vous, avez-vous déjà rencontré ce problème avec le serveur de build TFS 2010 ? Avez-vous eu d’autres soucis avec celui-ci ?

EDIT :

Attention, comme me l’a fait remarquer coq, si le fichier n’est pas protégé en modification, il est facile d’avoir une élévation de privilège. Il est possible de modifier le workflow du build et ainsi modifier le fichier bat : un utilisateur peut donc executer un script en tant qu’administrateur. Une solution propre serait donc d’utiliser un compte spécifique ayant juste le droit de relancer ce service.

Reflector – la fin de la connaissance gratuite ?

Reflector est un décompilateur .net, il permet de voir le code C# utilisé pour générer une assembly .net.

J’utilise cet outil quotidiennement, l’annonce du rachat par RedGate en aout 2008 (http://blog.lutzroeder.com/2008/08/future-of-net-reflector.html) m’avait fait froid dans le dos, tout le monde pensait qu’il deviendrait payant mais RedGate nous rassurait (LIEN). L’annonce de RedGate en février 2011 avait confirmé la crainte de bon nombre de personnes : Reflector allait devenir payant. Ce choix a été expliqué dans un billet de blog par RedGate : Why we reversed some of our Reflector decision.

En tant que développeur, nous sommes bien placé pour savoir que la maintenance et le développement d’un logiciel coute de l’argent, je comprends et respecte le choix de RedGate de rendre cet outil payant. Par contre, je ne suis pas d’accord avec leur politique commercial, je trouve dommage de rendre payant la version standard.

Cependant, je pense à tous les débutants désirant d’apprendre .net. Personnellement, j’ai appris ASP.net avec Reflector. Pour moi, cet outil est au moins aussi important que msdn, si ce n’est pas plus. Il m’a permis de bien comprendre le fonctionnement de .net. Lorsque je souhaitais reproduire un comportement que j’avais vu au sein de .net, il me suffisait de lancer Reflector et d’observer.

Jusqu’à maintenant, grâce aux versions express de Visual Studio, on pouvait développer en .net sans débourser un centime (à condition d’avoir une licence de Windows). Reflector faisait partie de ces outils gratuits indispensables pour comprendre le fonctionnement de .net ; je l’ai conseillé à plus d’un débutant …

Désormais, la version standard de Reflector coute 25€, je n’ai aucun souci à payer un logiciel pour lequel je connais ses avantages. Le prix n’est pas le problème, que ce soit 1€ ou 50€, c’est la même chose. Dans un monde où le savoir est gratuit et le devient de plus en plus, je vois ce changement comme la fin d’un accès gratuit à la connaissance.

Il s’agit d’une grande perte pour la communauté .net.

Heureusement, la communauté .net est réactive, de nombreuses solutions alternatives existent, je me suis intéressé à quelques unes d’entre elle.

Quelles alternatives ? 

Je ne suis pas le seul à faire ce constat, plusieurs alternatives ont vu le jour, parmi celles-ci, on peut noter :

Il s’agit d’un outil gratuit de la part d’un éditeur bien connu du monde .net. Je l’ai testé quelques instants, j’ai malheureusement eu plusieurs erreurs. Au niveau des fonctionnalités, la plupart des fonctionnalités présentes dans Reflector ont l’air d’être présente même si l’ergonomie et les performances sont encore à améliorés.

image

Les fonctionnalités de la version que j’ai testé sont assez limités, il n’est par exemple pas possible de naviguer dans le code, en cliquant sur sur les méthodes, on ne voit pas directement les types enfants, etc.

image

Contrairement aux 2 autres, ILSpy est un outil Open Source. Les sources sont consultables et la licence nous permet de “forker” le produit s’il ne nous convient pas ou plus. Ce point est rassurant, il y a peu de risque que cet outil devienne payant un jour et que l’on se retrouve bloqué comme avec Reflector. 

Au niveau des fonctionnalités, l’outil est très semblable à Reflector. On voit que les développeurs se sont clairement inspirés de l’outil original.

image

De nombreuses versions sortent ces dernières semaines, ceci s’explique par le fait que comme moi, beaucoup de personnes sont en train de chercher une alternative à Reflector.

 

D’autres alternatives sont listées sur ce lien : Alternatives To .Net Reflector 

Bilan

Au final, on se rend compte que de nombreux outils gratuits existent. Certes, ils ne sont pas aussi riche que Reflector, il n’y a pas encore autant de plugins, mais ils ont l’air prometteur. D’ici quelques mois, nous aurons, j’espère, un outil qui rivalise largement avec Reflector ; avec peut être des fonctionnalités complétement nouvelles qui permettra de nouveaux usages.

Finalement, on peut se dire que c’est une bonne chose que Reflector devienne payant. Celui-ci n’avait pas vraiment de concurrence, l’outil n’évoluait plus vraiment … La concurrence n’a jamais fait de mal.

A la question : est-ce que je vais acheter une licence de Reflector ? ma réponse est non, le problème n’est pas le prix, c’est plus dans la façon de faire. Je pense que je vais boycotter cet outil, non pas sans pincement au coeur. Actuellement, mon choix se porte sur ILSpy, je vais tester cet outil quelques instants et voir ce que cela nous dit.

Et vous ? Quels sentiments ressentez-vous par rapport à la non gratuité de Reflector ? Allez-vous continuer d’utiliser Reflector ? Quel décompilateur allez-vous utiliser ?

Posted: vendredi 20 mai 2011 10:34 par cyril | 4 commentaire(s)
Classé sous :
Oracle et transaction distribuée : Internal data error -3000

Je travaille actuellement sur un projet utilisant 2 instance de bases de données Oracle. J’avais besoin d’avoir une transaction globale, qui s’effectue sur l’ensemble de mes bases de données : une transaction distribuée.

J’ai pour cela utilisé l’objet TransactionScope de .net qui permet d’avoir des transactions distribuées en utilisant Ms DTC (Microsoft Distributed Transaction Coordinator). L’avantage de l’objet TransactionScope et qu’il n’y a rien à configurer, à partir du moment où MsDTC est configuré, il gère automatiquement les transactions.

Ce billet ne traite pas de la classe TransactionScope et de MsDTC, pour en savoir plus, je vous invite à lire l’article suivant : Implementing an Implicit Transaction using Transaction Scope

Afin d’accéder à mes bases de données Oracle, j’utilise le provider fourni par Oracle : ODP.net (Oracle Data Provider for .net).

Le code que j’utilise est le suivant :

static void Main(string[] args)
{
 
    String connectionString = @"DATA SOURCE=(DESCRIPTION=(CID=GTU_APP)(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.XXX.XXX)(PORT=1521)))(CONNECT_DATA=(SID=xxx1)(SERVER=DEDICATED)));
                                Password=xxxa;USER ID=xxxa;";
    String connectionString2 = @"DATA SOURCE=(DESCRIPTION=(CID=GTU_APP)(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.XXX.XXX)(PORT=1521)))(CONNECT_DATA=(SID=xxx2)(SERVER=DEDICATED)));
                                Password=xxxb;USER ID=xxxb;";
 
    Stopwatch watcher = Stopwatch.StartNew();
 
    Console.WriteLine(String.Format("0. {0:N0}", watcher.ElapsedMilliseconds));
 
    using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
    {
        using (OracleConnection conn = new OracleConnection(connectionString))
        {
            conn.Open();
            Console.WriteLine(String.Format("1. {0:N0}", watcher.ElapsedMilliseconds));
        }
 
        using (OracleConnection conn2 = new OracleConnection(connectionString2))
        {
            conn2.Open();
            Console.WriteLine(String.Format("2. {0:N0}", watcher.ElapsedMilliseconds));
        }
 
        scope.Complete();
    }
}

Pour que cela fonctionne, il est nécessaire d’installer le service “Oracle Services for Microsoft Transaction Server”. Ce composant est installable depuis l’installateur du client Oracle, il permet de faire l’interface entre MsDTC et Oracle.

Malheureusement, après l’installation de ces composants, j’obtenais l’erreur “Internal data error : – 3000” lorsque j’exécutais le code.

Après plusieurs recherches, l’erreur se situe au niveau de la chaine de connexion. Le code ci-dessus utilise le format de chaine de connexion complet, c’est à dire que l’on n’utilise pas le fichier tnsname.ora pour spécifier l’instance Oracle.

Afin de résoudre le problème, il est nécessaire d’utiliser le format de chaine de connection court : utiliser le fichier tnsname.ora.

Le fichier tnsname.ora se trouve dans le dossier $ORACLE_HOME$\product\11.2.0\client_1\NETWORK\ADMIN. Si vous n’avez pas ce dossier, il faut installer le composant “Oracle Connection Manager”.

Vous pouvez avoir plus d’informations sur les fichiers tnsname.ora ici : http://www.orafaq.com/wiki/Tnsnames.ora.

Au final, voici les composants du client Oracle que j’ai installés :

image

Il s’agit d’ailleurs d’un bug connu de Oracle référencé sous le numéro Bug 9788105 - ODP.NET WITH SYSTEM.TRANSACTIONS FAILS WHEN USING FULLY QUALIFIED TNS CON STRING.

Et vous ? Avez vous déjà travaillé avec des transactions distribuées et Oracle ?

Posted: mardi 3 mai 2011 23:36 par cyril | 0 commentaire(s)
Classé sous : ,
Base de données : pour ou contre le on delete cascade / set null

Lorsque l’on conçoit une applicaiton avec une base de données, nous utilisons généralement un modèle de donnée contenant des contraintes d’intégrités, notamment des clés étrangères (foreign key).

La suppression d’un enregistrement peut alors s’averer compliquée puisqu’il faut supprimer, ou mettre une valeur par défaut, toutes les lignes référencées par la ligne que l’on souhaite supprimer. Par exemple, si on souhaite supprimer un utilisateur, il faudra d’abord supprimer ces adresses, etc.

Plusieurs solutions sont possibles :

  • Soit on rajoute un champ IsDeleted permettant d’indiquer l’état de la ligne.
  • Soit on supprime la ligne et toutes les dépendances associés
  • Soit on supprime la ligne et on définit une valeur par défaut sur les lignes référencés

Sous SQL Server, il est possible d’effectuer des actions sur la ligne référencée lorsque l’on supprime un enregistrement : on peut soit supprimer la ligne référencée, soit mettre la valeur par défaut, soit mettre la valeur à null.

image

Ce comportement est surement valable pour d’autres bases de données.

Je travail actuellement avec un modèle composé d’une centaine de tables. A part pour certain cas particulier, je ne souhaite pas conserver l’information, de rajouter un champ “IsDeleted”.

Lorsque je supprime certains enregistrement, plusieurs dizaines de tables peuvent être impactées : effet “boule de neige”. Je me suis alors posé la question de l’utilisation de supprimer en cascade ou mettre certaines valeurs à null. J’utilise Entity Framework et ne souhaite pas utiliser de procédures stockées.

En point positif, j’ai noté :

  • la simplicité d’utilisation par rapport à du code.

En point négatif, j’ai noté :

  • Le risque de supprimer une bonne partie de la base de données en cas de mauvaise configuration
  • Le côté magique : le développeur a moins l’impression de controler ce qu’il se passe au niveau du code.
  • Le déport de logique : la base de donnée decider des actions qu’elle souhaite faire, elle fait plus que ce que le code lui demande.
  • le côté mythe : “c’est comme un trigger : c’est mal“

Je n’ai pas fait de test concernant la différence de performance entre la suppression des relations depuis le code et le on delete cascade.

Au final, j’ai décidé d’utiliser le “on delete” des clés étrangères. Les développeurs ne peuvent pas ajouter eux même ce comportement, seul les responsables du schéma peuvent effectuer ces modifications.

Et vous, qu’en pensez vous ? êtes vous pour ou contre l’utilisation du “on delete” sur les clés étrangères ?

Plus de Messages Page suivante »


Les 10 derniers blogs postés

- SharePoint 2013: Préparation de la migration - Création des site Templates dans 2010 et 2013 par Blog Technique de Romelard Fabrice le il y a 1 heure et 50 minutes

- [ #Yammer ] How to change interface language ? Comment changer la langue de l’interface ? par Le blog de Patrick [MVP SharePoint] le il y a 4 heures et 0 minutes

- Onedrive Sync Engine Host : CPU à 100% par Le petit blog de Pierre / Pierre's little blog le 08-06-2014, 22:22

- SharePoint : Bug sur la gestion des permissions et la synchronisation Office par Blog Technique de Romelard Fabrice le 07-10-2014, 11:35

- SharePoint 2007 : La gestion des permissions pour les Workflows par Blog Technique de Romelard Fabrice le 07-08-2014, 11:27

- TypeMock: mock everything! par Fathi Bellahcene le 07-07-2014, 17:06

- Coding is like Read par Aurélien GALTIER le 07-01-2014, 15:30

- Mes vidéos autour des nouveautés VS 2013 par Fathi Bellahcene le 06-30-2014, 20:52

- Recherche un passionné .NET par Tkfé le 06-16-2014, 12:22

- [CodePlex] Projet KISS Workflow Foundation lancé par Blog de Jérémy Jeanson le 06-08-2014, 22:25