Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

Je continue cette série de posts concernant les frameworks des test unitaires dans le développement BDD. Cette fois ci j’ai essayé MSpec que je ne connaissais pas très bien. Vous pouvez lire les postes précédents qui traitement des tests unitaires façon BDD:

  • [Tests] ASP.NET MVC 2 et les Tests Unitaires avec NBehave
  • [Tests] ASP.NET MVC 2 et les Tests Unitaires avec NBehave – Suite
  • [Tests] Ma quête BDD continue. Tests Unitaires avec SpecFlow

    Dans ce poste je vais également partir du même exemple (template par défaut de ASP.NET MVC 2) afin que vous puissiez comparer les différents frameworks des test unitaires BDD. Cette fois-ci je vais utiliser MSpec (machine.Specifications) qui est un framework de tests dont le but est de purifier et simplifier le code des tests. En fait plutôt que d’utiliser les méthodes et les attributs, MSpec utilise les délégués et les méthodes anonymes, cela bien évidement doit rendre votre code plus lisible. De plus, chose importante, une pleine intégration avec ReSharper 4.5 et 5.0.

    Pour commencer vous pouvez télécharger Mspec :

    Dans la version binaire vous avez l’installateur pour ReSharper. La syntaxe de MSpec est très simple. Tout ce que vous devez savoir sont les trois mots clés:

    • Establish
    • Because
    • It

    Le code de test de notre controlleur ASP.NET MVC est très simple donc je pense qu’une description détaillée n’est pas nécessaire (cependant laissez-moi un commentaire si vous voulez connaître les détails):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    public class AccountControllerTests3 
    {
    protected static AccountController controller;
    static IFormsAuthenticationService formsService;
    static IMembershipService membershipService;
    protected static ActionResult result;
    protected static LogOnModel model;


    Establish context = () =>
    {
    var controllerBuilder = new TestControllerBuilder();

    formsService = MockRepository.GenerateStub<IFormsAuthenticationService>();
    membershipService = MockRepository.GenerateStub<IMembershipService>();
    model = MockRepository.GenerateStub<LogOnModel>();

    controller = new AccountController(formsService, membershipService);
    controllerBuilder.InitializeController(controller);

    };


    Because user_logs = () =>
    {
    bool rememberMe = false;

    membershipService.Stub(
    x => x.ValidateUser("bdd", "mspec")).Return(true);
    formsService.Stub(x => x.SignIn("bdd", rememberMe));

    controller.ModelState.IsValid.ShouldBeTrue();
    };
    }

     



    [Subject(typeof(AccountController), "LogInTests")]
    public class When_logging_into_application_with_good_login_and_password : AccountControllerTests3
    {
    private It user_should_be_redirected_to_the_home_page = () =>
    {
    model.UserName = "bdd";
    model.Password = "mspec";

    result = controller.LogOn(model, string.Empty);

    result.AssertActionRedirect().ToAction<HomeController>(
    x => x.Index());
    };
    }
     
     


    [Subject(typeof(AccountController), "LogInTests")]
    public class When_logging_into_application_with_bad_login_and_password : AccountControllerTests3
    {
    It The_error_message_should_be_shown = () =>
    {
    model.UserName = "BAD";
    model.Password = "BAD";

    result = controller.LogOn(model, string.Empty);

    controller.ModelState[""].Errors[0].ErrorMessage.ShouldEqual(
    "The user name or password provided is incorrect.");
    };
    }

    Si on exécute nos tests dans ReSharper test runner nous obtenons le résultat suivant :

    MSpec

    Personnellement je ne sais pas si le code écrit est plus clean. Cela peut dépendre des cas. MSpec est une alternative intéressante mais pour le moment mon outils préféré reste quand même SpecFlow. Si parmi vous il y a des pros de MSpec, n’hésitez pas à me faire un retour pour améliorer mon test ou le réécrire. En tout cas c’est un outils intéressant dont je vais suivre l’évolution.

    A bientôt,

  • Depuis la toute première apparition de MEF sur codeplex les ambigüités fusent de tous les côtés. Les questions les plus souvent posées sont :

    • Quelle est la différence entre un IoC/MEF?
    • Dois-je utiliser MEF comme un IoC?
    • Peut-on utiliser MEF avec un autre IoC déjà en place?
    • MEF est il un IoC?

    Bien que je n’ai pas la prétention d’être un expert de MEF, voici comment je le comprends à travers son utilisation et les projets que j’ai fait (suis en train de faire), les discussions sur twitter et mes lectures :). Ce post ne rentre pas dans les détails techniques, c’est juste un point de départ pour une nouvelle discussion.

    1. Trois mots clés “Extensibility”, “Discovery”, “Composition”.

    MEF se concentre plus particulièrement sur l’extensibilité et la composition. Son but premier est d’étendre les applications en par exemple ajoutant tout simplement un binaire dans un répertoire prédéfini. MEF propose un mécanisme unifié pour atteindre ce but. Le développeurs n’ont plus besoin de réinventer la roue à chaque fois quand un mécanisme similaire est nécessaire.

    Donc, MEF en quelque sort peut-être considéré comme un Framework des “plug-ins” quand l’auteur de l’application n’est pas le même (dans la pratique l’auteur de l’application peut coder le deux) que l’auteur du plug-in et que ni l’application ni le plug-in n’ont aucune autre connaissance à part l’interface publiée (contract).

    2. Comment tout ceci se rapporte à IoC ?

    Pour moi la principale différence (non technique mais fonctionnelle) est que MEF est généralement utilisé pour gérer les “instances” que vous ne connaissez pas et dont vous n’avez rien à faire quel type concret sera derrière l’interface, alors qu’un containeur IoC sert à gérer les “instances” que vous connaissez.

    Vous pouvez par exemple avoir enregistré dans votre containeur un Logger pour l’utilisation dans votre application et un autre Logger pour le besoin des tests unitaires. Potentiellement avec MEF vous pourrez avoir de 1 à N Loggers dont vous ne connaissez pas le type et l’implémentation. Encore une fois, l’intention de MEF est de gérer l’extensibilité par plug-in anonymes ce que le différencie d’un conteneur IoC standard.

    La où le conteneur IoC est très bon c’est le contrôle de votre code au bas niveau, contrôle de la dépendance des grappes d’objets, gestion de la vie des instances, gestion de l’AOP et des décorateurs.

    3. L’un sait faire ce que l’autre ne sait pas faire (et vice versa :))

    Effectivement il y a des fonctionnalités qui peuvent être implémentées avec MEF ou un containeur IoC avec plus ou moins d’effort mais comme je l’ai souligné au début de l’article le but de MEF n’est pas le même.

    Même dans mes petits projets je favorise l’utilisation d’un containeur IoC. Une de bonnes pratiques d’utilisation d’un containeur IoC et que vous le référencez uniquement dans un endroit appelé le CompositionRoot. Dans la plupart des applications cela est un code lancé au démarrage d’une application comme par exemple Global.asax ou un HttpModule. Ce la veut dire que votre code ne dois plus jamais référencer le conteneur dans un autre endroit. Tout la magique d’un conteneur IoC est qu’il enregistre les objets et leur dépendances et permet de les construire et nous les fournir dès qu’on en a besoin.

    Un petit problème

    Cependant cela peut poser un petit problème. Dans les applications très larges la configuration centrale peut donner mal à la tête car elle peut vite devenir très difficile à lire et à comprendre. De plus, la configuration centrale peut-être assimilé à des variables globales qui sont accessibles à tout le monde et vous ne savez plus qui les utilise et quand. Bien que la plupart des containeurs IoC donne la possibilité de gérer les sous containeurs dans la pratique il est très difficile de les gérer indépendamment.

    Enfin, dans une large application nous risquons de nous retrouver avec des containeurs IoC qui gèrent des centaines des composants enregistrés alors que la plupart des opérations dans l’application n’en utilise que quelque uns. C’est là où je vois la limitation d’un containeur IoC.

    MEF again

    Ce que permet MEF c’est de diviser votre système en morceaux plus petits. MEF permet de coder les applications comme une série des applications plus petites et quand on relie toutes les applications nous obtenons l’ensemble des fonctionnalités. Comme on a dit, MEF permet la découverte des plug-ins (extensions). Il dispose d’un mécanisme qui en se servant de la metadata peut accéder aux extensions. MEF permet de décorer les extensions avec une metadata additionnelle qui permet les requêtage et le filtres puissants. Combiné avec une capacité de faire du lazy load sur les extensions, d’interroger la metadata avant de charger l’extension permet de scénarios intéressants comme par exemple le versionning des plug-ins.

    Conclusion

    Je pense que l’utilisation de l’un n’exclu pas l’utilisation de l’autre. Le containeur IoC et MEF ne sont pas destinés pour faire la même chose. Faire cohabiter les deux est une chose un peu plus difficile car il faut bien réfléchir à des aspects qui ne se posent pas lorsque vous développez avec l’un ou l’autre. Quel containeur prend en charge la vie des instances ? Est-ce que MEF peut accéder aux service IoC et comment ? Est-ce que le containeur IoC peut accéder aux exports MEF ? Cela rajoute de la complexité donc réfléchissez bien avant de vous lancer. Il y a des efforts qui sont fait dans ce sens dans MEFcontrib où par exemple une intégration MEF/Unity est possible. De même Castle Windsor est en train de travailler sur le même genre d’intégration.

    A bientôt

    Dans mes posts précédents j’ai commencé à parler des tests unitaires avec NBehave. Si vous êtes curieux vous pouvez y jeter un œil mais cela n’est pas nécessaire pour comprendre cet article. Quoique pour les exemples ça peut servir:

    Cette fois ci, je vais reprendre l’exemple précédent et l’utiliser avec SpecFlow. SpecFlow permet d’écrire des scénarios dans les exigences BDD. Le langage utilisé est compatible avec Gherkin qui est un Business Redeable Domain Specific Language. Gherkin est compris par Cucumber qui est un autre outils pour le développement BDD. Ce qui est important ce que nous n’avons pas besoin d’avoir notre propre environnement d’exécution (comme la console NBehave dans l’exemple précédent) mais NUint s’en chargera pour nous.

    Note: Avec SpecFlow on ne parle plus de Story mais de Feature (exigence Gherkin).

    Passons donc à la pratique.

    1. Téléchargez SpecFlow ici et installez-le.

    2. Ajoutez une référence vers le TechTalk.SpecFlow.dll dans le projet qui contiendra vos Features (dans le projet de test en l’occurrence).

    3. Ajoutez le fichier de feature écrite au format Gherkin. Ce qui est super ce qu’en installant SpecFlow vous disposer maintenant des templates dans Visual Studio pour créer votre feature.

    SpecFlow_visualStudio_integration

    Une feature avec des scénario par défaut est proposé. Nous allons le changer par le notre. Celui que nous avons utilisé avec NBehave (en renomme juste Story en Feature)

    Feature: Handle user log in
        In order to check authentication
        As a user
        I want the system to check if user can log in

    Scenario: User logs in

        Given I would like to log in
        When I log in as bdd with a password nbehave without remembering me
        Then I should be redirected to the home page


    Scenario: User doesn't log in
        Given I would like to log in
        When I log in as BAD with a password BAD without remembering me
        Then The error message should be shown

     

    Comme vous pouvez le constater SpecFlow génère lui même le code qui fait l’intégration de nos tests dans NUnit !

    SpecFlow_VisualStudio_Integration2

    4. Maintenant nous implémentons notre classe de test. Je l’ai appelé AccountControllerTests2 et par rapport à la classe que nous avons utilisé avec NBehave seulement les choses suivantes changent :

    • L’attribut ActionSteps a été remplacé par l’attribut Binding.
    • Les paramètres sont passés à l’aide de la chaîne "(.*)” et non le caractère $ comme dans l’exemple précédent.

    Et (puis) c’est TOUT :)

    Voici la classe complète :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    [Binding] 
    public class AccountControllerTests2
    {
    private AccountController controller;
    private IFormsAuthenticationService formsService;
    private IMembershipService membershipService;
    private ActionResult result;
    private LogOnModel model;

    private void SetUpController()
    {
    var controllerBuilder = new TestControllerBuilder();

    formsService = MockRepository.GenerateStub<IFormsAuthenticationService>();
    membershipService = MockRepository.GenerateStub<IMembershipService>();
    model = MockRepository.GenerateStub<LogOnModel>();

    controller = controllerBuilder.CreateController<AccountController>(new object[] {formsService, membershipService});
    }

    [Given(@"I would like to log in")]
    public void AccesAccountController()
    {
    SetUpController();
    controller.ShouldNotBeNull();
    }

    [When(@"I log in as (.*) with a password (.*) without remembering me")]
    public void LogIn(string username, string password)
    {
    bool rememberMe = false;

    membershipService.Stub(x => x.ValidateUser(username, password)).Return(true);
    formsService.Stub(x => x.SignIn(username, rememberMe));

    model.UserName = "bdd";
    model.Password = "nbehave";

    controller.ModelState.IsValid.ShouldBeTrue();

    result = controller.LogOn(model, string.Empty);
    }

    [Then(@"I should be redirected to the home page")]
    public void RedirectToHomePage()
    {
    result.AssertActionRedirect().ToAction<HomeController>(x => x.Index());
    }

    [Then(@"The error message should be shown")]
    public void ShowErrorMessage()
    {
    controller.ModelState[""].Errors[0].ErrorMessage.ShouldEqual("The user name or password provided is incorrect.");
    }
    }

     

    5. Exécutez vos tests ! C’est super, l’intégration est complète avec NUnit et par conséquent avec ReSahrper.

    SpecFlow_ReSharper_OK

    Juste pour vous montrer que le test échoue bien lorsque le mot de passe fourni n’est pas bon :

    SpecFlow_ReSharper_KO

    Conclusion

    Avec l’outils comme SpecFlow le développement BDD devient encore plus facile. L’intégration complète avec Visual Studio NUnit et donc ReSharper est importante car on n’a pas besoin d’exécuter des outils externes comme dans le cas de NBehave.

    J’espère que SpecFlow va évoluer. Il est prévu entre autres l’intégration avec MSTests.

    Sur le site de SpecFlow vous trouverez également une version pour Visual Studio 2010.

    UPDATE: On vient de me tweeté que SpecFlow supporte les scénarios écrits en Anglais, Français, Allemand, Hongrois, Hollondais et Suédois. Donc vous n'êtes pas obligé d'utiliser l'anglais

    Bons tests et à bientôt :)

    Après une petite introduction dans mon post [Tests] ASP.NET MVC 2 et les Tests Unitaires avec NBehave aux tests unitaires avec NBehave. J’ai décidé de terminer mon exemple en employant la nouvelle syntaxe de NBehave. Cette fois ci je teste la classe AccountController du template de création de l’application web ASP.NET MVC 2.

    De quelle nouvelle syntaxe on parle ?

    Si vous vous rappelez l’exemple précédent nous avons utilisé la syntaxe fluent pour définir nos scenarios. En fait, cette syntaxe n’est plus conseillée ce que personnellement je trouve un peu dommage. Il est dorénavant préconisé d’utiliser les attributs Given/When/Then directement pour décorer les méthodes.

    De plus les Stories et les Scenarios doivent être décrits dans un fichier texte externe ce qui permet de modifier les scenarios de test sans recompiler le code.

    La partie pratique

    Tout d’abord définissons nos scenarios de tests dans un fichier texte externe. Pour le besoin de notre exemple je crée un fichier AccoutController.scenario que j’inclus dans mon projet de tests que se présente comme ceci:

    NbehaveSolution

    Le contenu de ce fichier contient une Story et deux Scenarios pour tester que l’utilisateur peut se logeur avec un login et un mot de passe valide et qu’un message d’erreur sera affiché dans le cas où l’utilisateur ne possède pas de login et/ou de mot de passe valide:

    Story: Handle user log in

    Scenario: User logs in

        Given I would like to log in
        When I log in as bdd with a password nbehave without remembering me
        Then I should be redirected to the home page


    Scenario: User doesn't log in
        Given I would like to log in
        When I log in as BAD with a password BAD without remembering me
        Then The error message should be shown

    Comme vous pouvez le constater les scenarios ne contiennent aucune ligne de code et sont compréhensibles par des utilisateurs non techniques.

    Maintenant définissons notre classe de test pour les scenarios ci-dessus. La classe doit être décoré avec l’attribut ActionsSteps afin d’interpréter ce fichier de texte. Une correspondance est fait entre chaque ligne de texte et les méthodes décorées avec les attributs correspondants. Donc par exemple la phrase :

    Given I would like to log in

    sera mappée soit à la méthode décorée comme ceci :

    1
    2
    [Given("I would like to log in")] 
    public void AccesAccountController()

    soit à la méthode qui sera appelé de cette manière :

    1
    2
    [Given] 
    public void I_would_like_to_log_in()

    Je crois également que le CamelCase marche au lieu des “_”.

    Le but de ce post n’est pas l’explication de la manière dont les tests ont été écrits mais si vous avez des questions n’hésitez pas à me faire un commentaire. En tout cas avec NBehave nos tests peuvent ressembler à ceci:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    [ActionSteps] 
    public class AccountControllerTests
    {
    private AccountController controller;
    private IFormsAuthenticationService formsService;
    private IMembershipService membershipService;
    private ActionResult result;
    private LogOnModel model;

    private void SetUpController()
    {
    var controllerBuilder = new TestControllerBuilder();

    formsService = MockRepository.GenerateStub<IFormsAuthenticationService>();
    membershipService = MockRepository.GenerateStub<IMembershipService>();
    model = MockRepository.GenerateStub<LogOnModel>();

    controller = controllerBuilder.CreateController<AccountController>(new object[] {formsService, membershipService});
    }

    [Given("I would like to log in")]
    public void AccesAccountController()
    {
    SetUpController();
    controller.ShouldNotBeNull();
    }

    [When("I log in as $username with a password $password without remembering me")]
    public void LogIn(string username, string password)
    {
    bool rememberMe = false;

    membershipService.Stub(x => x.ValidateUser(username, password)).Return(true);
    formsService.Stub(x => x.SignIn(username, rememberMe));

    model.UserName = "bdd";
    model.Password = "nbehave";

    controller.ModelState.IsValid.ShouldBeTrue();

    result = controller.LogOn(model, string.Empty);
    }

    [Then("I should be redirected to the home page")]
    public void RedirectToHomePage()
    {
    result.AssertActionRedirect().ToAction<HomeController>(x => x.Index());
    }

    [Then("The error message should be shown")]
    public void ShowErrorMessage()
    {
    controller.ModelState[""].Errors[0].ErrorMessage.ShouldEqual("The user name or password provided is incorrect.");
    }
    }

    A noter que notre méthode SetUpController() prépare notre contrôleur de test en lui passant tous les objets fake nécessaires.

    Vous pouvez également passer les paramètres à vos méthodes à partir du fichier de texte en les marquant avec “$” comme dans la phrase ci dessus: I log in as $username with a password $password without remembering me. Le mot trouvé dans le fichier texte sera passé en paramètre à la méthode qui d’ailleurs doit accepter ces paramètres dans sa signature.

    Exécution des tests

    Maintenant il faut qu’en exécute nos tests pour s’assurer que tout fonctionne correctement. Pour cela nous allons utiliser la console fournie avec NBehave. La ligne de commande suivante permet d’exécuter nos tests:

    NBehave-Console.exe MvcApplication.Tests.dll /sf=Specs\AccountController.scenario

    Remarque: En paramètre sf nous passons notre fichier texte contenant des scénarios.

    Si tout se passe bien vous devriez avoir le résultats suivant:

    NBehave-Console_Results

    Conclusion

    Personnellement j’aime bien l’approche BDD. Je trouve que le fait d’écrire des scénarios et des stories permet de mieux se concentrer sur la fonctionnalité métier que nous voulons tester. Concernant la nouvelle syntaxe de NBehave il y a des avantages et des inconvénients.

    Avantages

    • les tests sont plus facile à lire car sont écrit dans un langage naturel.
    • le code de tests est plus facile à lire également.
    • les stories et les scenarios peuvent être écrits par des personnes non techniques.
    • possibilité de modifier les valeurs des tests sans recompiler le code.

    Inconvénients

    • pour le moment on est obligé d’utiliser la console de NBehave pour exécuter les tests. Je n’ai pas trouvé comment les lancer avec ReSharper ou NUnit. Si vous avez des infos là dessus je suis preneur.
    • il faut tout de même modifier et recompiler le code si vous ajoutez une phrase Given/Then/When qui n’est pas mappé dans le code. Je veux dire par là que la l’utilisation du fichier texte est quand même limité.

    Pour ma part, l’utilisation de NBehave est très satisfaisante. N’hésitez pas à y jeter un œil car ça vaut vraiment le coup.

    A bientôt :)

    Juste un simple poste pour vous dire que...

    HighOnCoding vous propose de gagner un livre Pro ASP.NET MVC par Steven Sanderson tout simplement en tweetant :

    HighOnCoding giving away Pro ASP.NET MVC by Steven Sanderson. Winner announced Jan 31st. RT to WIN your copy #highoncoding

    Les resultats seront annoncés le 31/01/2010

    A+

    Dans mon poste précédent [Design Patterns] Partie 1: SRP: Single Responsibility Principle j’ai vous ai montré comment appliquer le principe SRP au niveau de l’application. Cependant comme je vous ai expliqué le principe SRP s’applique également au niveau d’une seule classe. Prenons un exemple suivant avec notre classe Product:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public class Product 
    {
    public string Name { get; set; }
    public decimal? Price { get; set; }
    public int? UnitsInStock { get; set; }

    public Product(string name, decimal? price, int? unitsInStock)
    {
    Name = name;
    Price = price;
    UnitsInStock = unitsInStock;
    }

    public Product ApplyDiscount(bool applyDiscount)
    {
    // 2: calcul de la remise
    var discount = applyDiscount ? .90m : 1;

    return new Product(this.Name, this.Price * discount, this.UnitsInStock);
    }
    }

    J’ai dans cette classe une méthode ApplyDiscount(bool applyDiscount) qui permet de calculer une ristourne pour le produit. Tout va bien. Maintenant si j’ajoute une nouvelle méthode pour calculer le taux de la TVA par exemple :

    1
    2
    3
    4
    public Product ApplyTax() 
    {
    return new Product(this.Name, this.Price * 1.196m, this.UnitsInStock);
    }

    Logiquement notre classe a 2 responsabilités maintenant. Elle calcule la ristourne et la taxe. Si jamais nous devons changer la manière dont est calculé la taxe ou la ristourne nous devons obligatoirement modifier cette classe. Bien que les méthodes de calcul semblent être bien situées dans la classe Product, il est cependant préférable d’externaliser le calcul dans des classes externes. Il est très probable que la manière dont est calculé la taxe et la ristourne change. De plus nous pourrions plus facilement d’étendre notre modèle si nous devons définir plusieurs algorithmes de calcul et d’implémenter le pattern Stratégie. Pour cela par exemple pour calculer la ristourne nous pouvons créer la classe DiscountProductCalculator :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class DiscountProductCalculator 
    {
    public static Product GetDiscountedProduct(Product initialProduct, bool applyDiscount)
    {
    // 2: calcul de la remise
    var discount = applyDiscount ? .90m : 1;

    initialProduct.Price = initialProduct.Price * discount;

    return initialProduct;
    }
    }

    Nous modifions ensuite la méthode ApplyDiscount(bool applyDiscount)dans la classe Product afin d’utiliser notre nouvelle classe destinée à calculer la ristourne:

    1
    2
    3
    4
    public Product ApplyDiscount(bool applyDiscount) 
    {
    return DiscountProductCalculator.GetDiscountedProduct(new Product(this.Name, this.Price, this.UnitsInStock), applyDiscount);
    }

    Analogiquement nous pouvons créer une classe pour calculer la taxe (TaxProductCalculator) et modifier la méthode ApplyTax() afin d’appeler notre nouvelle classe pour calculer la taxe. Je ne mets pas le code ici car la démarche est exactement la même que dans le cas de la ristourne.

    Conclusion

    Dorénavant si nous devons changer le mode de calcul pour la ristourne ou la taxe nous ne modifions que les classes correspondantes (en admettant que les appels des méthodes des ces classes de changent pas, sinon il faudrait aussi changer l’appelant donc la classe Product).

    Comme vous pouvez le voir le SRP s’applique également au niveau d’une classe simple. Bien que c’est une bonne pratique d’attribuer une responsabilité à une classe, il n’est parfois pas possible de le faire. Pour cela il ne faut pas tomber dans la paranoïa et essayer à tout prix de respecter ce principe. Il est important de juger les parties qui changent souvent et les encapsuler dans des classes externes.

    A bientôt

    image

    l’image empruntée de LosTechies.

    Pour continuer le sujet que j’ai introduit dans un de mes posts précédents [Design Patterns] Est-ce que votre code est S.O.L.I.D(e) - Introduction je m’attaque à la première bonne pratique:

    Single Responsibility Principle (SRP) : Principe de Responsabilité Unique.

    “Une classe ne doit avoir qu'une raison pour qu'elle change”

    Qu’est-ce que cela veut dire ?  Chaque développeur dans son travail de tous les jours a des problèmes plus ou moins complexes à résoudre. Il est souvent très difficile de résoudre un problème en le traitant dans sa globalité, surtout quand les paramètres qui le définissent changent de temps en temps. En découpant le “gros” problème en des mini-problèmes permet de les résoudre beaucoup plus facilement. En fait, chaque mini-problème peut être assimilé à une tâche concrète qui à son tour peut être implémentée à l’aide d’une simple classe. Puisque chaque classe a une simple tâche à accomplir il y a par conséquence une seule raison pour qu’elle change. Nous pouvons donc changer juste cette classe si la tâche correspondante change. Comme vous-vous en doutez une tâche = une responsabilité.

    Cela signifie également que nous devons apprendre à découper les problèmes car les problèmes complexes sont difficile à résoudre en tant que l’ensemble indivisible. Cela permet de réduire la complexité de l’ensemble car chaque mini-problème peut être traité séparément.

    L’application de SRP est aussi bénéfique à la qualité de votre code ainsi qu’à la modélisation objet que vous faites. Pourquoi ? Car le code est:

    • plus robuste.
    • plus facile à lire et à comprendre.
    • plus facile à tester et à maintenir.
    • plus facile à étendre avec de nouvelles fonctionnalités.

    Qu’en est-il dans la pratique ?

    Exemple initial

    Nous allons créer une petite application console pour illustrer le problème. A cette application console nous ajoutons une couche d’accès aux données qui est sous la forme d’une bibliothèque de classes. Dans ma couche d’accès aux données j’ajoute un Entity Framework Data Model basé sur la base de données Northwind et la table Products:

    srp_projets

    Admettons que nos règles métier nous demandes d’implémenter le scénario suivant:

    “Nous voulons récupérer une liste de produits (Products) en appliquant éventuellement 10% de remise suivant le cas.”

    Nous créons donc une classe ProductDAO qui nous permettra de récupérer notre liste de produit qui correspond à l’énoncé ci-dessus. La classe possède une méthode GetProducts(bool applyDiscount) qui prend en paramètre une valeur pour indiquer si on veut appliquer la ristourne. L’implémentation n’est pas un best practice donc ne me blâmez pas là-dessus mais elle peut ressembler à ceci :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
        public class ProductDAO 
    {
    private readonly NorthwindEntities dataContext;

    public ProductDAO()
    {
    // 1: création de la connexion
    dataContext = new NorthwindEntities();
    }

    public IEnumerable<Products> GetProducts(bool applyDiscount)
    {
    // 2: calcul de la remise
    var discount = applyDiscount ? .90m : 1;


    var products = (from p in dataContext.Products
    select p)
    .AsEnumerable();
                         

    // 3: requête de sélection
    return from p in products
    select new Products {
    CategoryID = p.CategoryID,
    Discontinued = p.Discontinued,
    ProductID = p.ProductID,
    ProductName = p.ProductName,
    QuantityPerUnit = p.QuantityPerUnit,
    ReorderLevel = p.ReorderLevel,
    SupplierID = p.SupplierID,
    UnitPrice = p.UnitPrice * discount, // 4: appliquer la remise
    UnitsInStock = p.UnitsInStock,
    UnitsOnOrder = p.UnitsOnOrder
    };
    }
    }

    L’appel à cette classe se fait dans l’application console de la manière suivante :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    static void Main(string[] args) 
    {
    var productDAO = new ProductDAO();


    foreach (var p in productDAO.GetProducts(true, true))
    Console.WriteLine(p.ProductName);


    Console.ReadKey();
    }

    Comme vous pouvez le constater cette petite fonctionnalité nous permet de récupérer de produits et d’appliquer une remise de 10%. Mais est-ce qu’il y a pas quelque chose qui vous choque ? Bien entendu, la classe ProductDAO ne respecte pas de principe SRP car elle a trop de responsabilités ! Et pourtant il n’y a qu’une requête Linq !

    Cet exemple bien que banal, je l’ai vu implémenté d’une manière similaire à de nombreuses reprises sur divers projets. Pour des petits projet ceci peut-être n’est pas gênant mais d’une manière générale c’est une galère à maintenir. Nous avons dit à l’instant que cette classe ne respecte pas de principe SRP, donc elle a plus d’une responsabilité et donc par conséquence plus d’une raison de changer. Voyons les responsabilité qu’elle endosse (le numéros font référence aux commentaires dans le code) :

    1. Création de la connexion. De plus ceci suppose que la connexion est configurée correctement au niveau du fichier .config de votre application car nous utilisons un constructeur sans paramètres.
    2. Calcule de la remise. Le fait que le taux soit défini en dur c’est moche, mais je ne voulais plus d’ajouter des paramètres pour une question de simplicité. Le taux aurait pu être passé en paramètre ou récupéré d’une autre manière.
    3. L’exécution de la requête.
    4. L’application de la remise.
    5. La requête Linq implicitement faire référence à un schéma de données relationnel de la base de données.

    Potentiellement on a dans cette petite classe 5 raisons possible pour que cette classe change. Si par exemple la remise doit être également calculée en fonction da la catégorie du produit ? Si la connexion est passée par une autre classe ou un autre paramètre ?

    Toutes ces raisons nous feront modifier notre classe à chaque fois ! Pire encore, si les questions que nous-nous posons dépendent du contexte de l’appelant. Lorsque la méthode GetProducts est appelée d’une autre classe ces conditions peuvent être différentes que lorsqu’on l’appelle de notre application console. Il nous faudrait à ce moment là de gérer le contexte de l’appelant dans la classe par les biais de if/switch, ce qui est MAUVAIS.

    Analyse

    Lorsqu’on a implémenté notre couche de données, l’assistant d’Entity Framework a généré la classe Products à la volé. Le problème de cette implémentation est que le modèle métier est au niveau de la couche d’accès aux données. Ce n’est pas de la responsabilité de la couche d’accès aux données d’implémenter une classe comme Products. De même le fait de déterminer si la remise doit être appliquée c’est du ressort de la logique métier donc modèle métier.

    Attention: le but de ce poste n’est pas de critiquer ou de donner les best practices quant à l’utilisation de EF. On s’en sert pour démontrer le problème souvent rencontré dans beaucoup d’implémentations de projets. On aurait pu aussi bien utiliser ADO.NET, Linq2SQL ou autre chose pour illustrer le problème. Pour plus d’informations sur EF n’hésitez pas à jeter un œil sur le blog de Matthieu Mezil.

    Un autre problème est que la couche de présentation (application console) référence directement la couche d’accès aux données. C’est un sujet pour un autre post que je vais traiter bientôt.

    Ici je vais essayer de présenter les démarches de refactoring pour séparer les responsabilités qui dans l’exemple ci-dessus se trouvent au sein de la méthode GetProducts(bool applyDiscount).

    Refactoring 1

    Objet: “Introduire un modèle métier.”

    But: “Les objets du modèle métier ont souvent un comportement qui est déterminé par les règles métier. Ici, nous pouvons placer la responsabilité de décider si la ristourne doit être appliquée ou pas.”

    Tout d’abord il faut ajouter une couche qui contiendra notre modèle métier et nos objets métier (je n’utilise pas le mot clé entités pour ne pas confondre avec les entités du modèle de persistance crée avec EF). Notre couche métier (Domain) que j’ajoute à la solution de notre application ressemble maintenant à ceci:

    SRP_solution2

    La couche Domain ne doit référencer aucune autre couche.

    Ensuite nous définissons noter objet métier Product qui est un POCO (Plain Old Clr Object) tout simple. Le but de cette démarche est de découper la notion de la persistance du modèle métier car au final ce qui m’intéresse se sont les interactions entre les objets métier et non la manière dont les données sont persistées (BD, XML ou même un fichier texte).  Mon objet est tout simple et se présente comme ceci :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public class Product 
    {
    public string Name { get; set; }
    public decimal? Price { get; set; }
    public int? UnitsInStock { get; set; }

    public Product ApplyDiscount(bool applyDiscount)
    {
    // 2: calcul de la remise
    var discount = applyDiscount ? .90m : 1;

    return new Product(this.Name, this.Price * discount, this.UnitsInStock);

    }
    }

    Comme vous pouvez le constater à part les propriétés publiques les plus communes l’objet la classe possède juste une responsabilité qui est d’appliquer et de renvoyer un objet Product avec la ristourne.

    Refactoring 2

    Objet: “Introduire une abstraction pour récupérer et sauvegarder les produits à partir d’une source de données.”

    But: “Ce n’est pas de la responsabilité de l’objet métier de savoir comment il doit être récupéré et sauvegardé dans une source de données.”

    Ce qui se fait généralement pour ajouter de l’abstraction aux accès aux données est d’implémenter le pattern Repository. Nous devons donc définir une classe dans notre modèle métier. La classe que je vais définir s’appelle ProductRepository et se présente comme ceci:

    1
    2
    3
    4
    public abstract class ProductRepository 
    {
    public abstract IEnumerable<Product> GetProducts();
    }

    Cette classe tout simplement récupère une liste des produits (j’ai omis la sauvegarde car ce n’est pas le but de ce poste).

    A quoi cette classe nous servira ? Comme vous l’avez remarqué elle est abstraite. En fait puisque elle se trouve dans notre modèle métier elle servira de la classe de base pour les implémentations concrètes de Product repository. Dans notre cas nous implémentons EF donc dans notre couche d’accès aux données nous allons définir une class EntityProductRepository qui dérivera de la classe ProductRepository.

    Nous aurions pu définir également une classe XmlProductRepository et récupérer nos produits à partir d’un fichier XML si cela nous intéressait. Ce qu’il faut retenir et qu’un niveau de la couche Domain nous nous fichons comment les produits seront récupérés.
    Refactoring 3

    Objet: “Implémenter l’accès aux données”

    But: “Il est important de récupérer les données à partir d’une source de données. La seule responsabilité de cette classe est d’exécuter la requête sans se préoccuper des règles métier comme de savoir si une remise doit être appliquée ou pas.”

    Nous allons maintenant dans notre couche d’accès aux données (NorthwindDAL). Il faut implémenter une classe qui permettra de récupérer les produits à partir de la base de données. Elle s’appelle EntityProductRepository et dérive à partir de la classe abstraite ProductRepository. C’est une implémentation concrète de notre abstraction qui est manipulé par les autres couches. Elle se présente de cette manière:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public class EntityProductRepository : ProductRepository 
    {
    private readonly NorthwindEntities dataContext;

    public EntityProductRepository(string connectionString)
    {
    dataContext = new NorthwindEntities(connectionString);
    }

    public override IEnumerable<Product> GetProducts()
    {
    var products = (from p in dataContext.Products
    select p).AsEnumerable();

    return from p in products select p.ToDomainProduct();
    }
    }

    Rappelez-vous la classe que je vous ai présenté au début de ce post. Regardez comment maintenant la requête Linq est petite quand on commence à enlever les responsabilités et de les placer dans des classes à part ! Elle se contente juste de récupérer la liste de produits de la base de données.

    A noter cependant que le constructeur prend maintenant en paramètre la chaîne de connexion qui n’est plus maintenant récupéré directement par la classe. La chaîne de connexion est injectée dans le constructeur par le biais du pattern DI à partir de la l’application console (poor man DI mais qui marche bien dans cet exemple).

    La méthode d’extension (non présenté ici) ToDomainProduct() transforme juste les entités EF à nos vraies classes POCO de la couche du Domaine.

    Refactoring 4

    Objet: “Fournir les objets métiers à l’application console pour les afficher”

    But: “Maintenant que nous savons comment récupérer les produits de la base de données nous devons fournir un service qui permettra de les manipuler.”

    Ce service se trouvera au niveau de la couche Domain et utilisera notre classe ProductRepository pour manipuler les produits. La classe s’appelle ProductProvider  :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public class ProductProvider 
    {
    private readonly ProductRepository repository;

    public ProductProvider(ProductRepository repository)
    {
    this.repository = repository;
    }

    public IEnumerable<Product> GetDiscountedProducts()
    {
    return from p in this.repository.GetProducts()
    select p.ApplyDiscount(true);
    }
    }

    Comme vous pouvez le constater, la classe se sert juste de notre repository pour récupérer les produits avec la ristourne et les retourne à l’appelant. Vous constaterez que la classe ne fait rien d’autre que de demander les produits avec la ristourne. Le calcul de la ristourne est fait au niveau de la classe Product qui sait comment calculer la ristourne.

    Refactoring 5

    Objet: “Configurer l’application”

    But: “Nous devons maintenant récupérer la chaîne de connexion et d’instancier notre EntityProductRepository.”

    Cette responsabilité incombe à la classe configurateur que j’ai appelé Bootstraper et qui est évoquée dans le (composition root) de l’application (au niveau le plus haut, la où l’application démarre). Je l’appelle au début de la méthode Main de mon application console. La classe est toute simple:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public class Bootstrap 
    {
    public static ProductProvider ConfigureProducts()
    {
    string connectionString = ConfigurationManager.ConnectionStrings["NorthwindEntities"].ConnectionString;

    string productRepositoryTypeName = ConfigurationManager.AppSettings["ProductRepositoryType"];

    var productRepositoryType = Type.GetType(productRepositoryTypeName, true);

    var repository = (ProductRepository)Activator.CreateInstance(productRepositoryType, connectionString);

    return new ProductProvider(repository);
    }
    }

    Elle récupère la chaîne de connexion, instancie le repository concret et retourne notre ProductProvider qui permettra à l’application console d’afficher les produits. Remarquez comment nous injectons la chaîne de connexion dans la couche la plus basse. Notre ProductProvider est ensuite configuré avec les repository et retourné à l’application.

    Afficher les produits

    Notre application console se présente finalement de cette manière.

    1
    var productService = Bootstrap.ConfigureProducts();
    foreach (var p in productService.GetProducts()) 
    Console.WriteLine(p.Name + "|" + p.Price);

    Où sont nos responsabilités ?

    Comme vous l’avez remarquez nous avons découpé notre classe ProductDAO qui avait trop de responsabilités.

    1. Le calcul de la ristourne est de la responsabilité de la classe Product. Elle fait partie du modèle métier donc elle doit avoir la connaissance de cette règle métier.
    2. La classe ProductRepository est une abstraction qui permet de récupérer les produits à partir de la base de données. Les produits eux mêmes (Product) ignore complètement d’où ils viennent (base de données, xml, etc.). L’exécution de la requête sur la base de données est assurée par la classe EntityProductRepository.
    3. L’application de la remise est possible grâce à la classe ProductProvider.
    4. Configuration de l’application et la récupération de la chaîne de connexion est assurée par la classe Bootstrap.
    5. L’affichage (le formatage à l’affichage) est assuré par notre application console (GUI).

    Etant donné que les responsabilités sont plus clairement découpés le fait d’en modifier une n’entraîne pas de modification des autres classes.

    Conclusion

    Il n’est pas toujours possible d’avoir une responsabilité unique au niveau de la classe, le but et d’en avoir le moins possible et être conscient de nos choix lorsqu’on en introduit plus. Le SRP est un principe le plus simple à comprendre mais peut-être le plus difficile à mettre en place. Attention, ici j’ai donnée l’exemple avec les couches applicatives mais le SRP s’applique aussi bien au niveau d’une classe ou d’une seule assembly. Je vais faire un autre poste à ce sujet pour démontrer l’applicabilité de ce principe au niveau d’une seule classe.

    J’espère que cette explication est assez claire, n’hésitez pas à me laisser vos commentaire et vous remarques. Merci à ceux qui ont réussi à lire ce long post.

    A bientôt.

    Ahh, que cette année est (était) intéressante. Entre les sorties de Silverlight Toolkit, puis les Bétas de Visual Studio et plus généralement Framework 4.0, il y a un autre produit qui mérite notre attention et dont on parle un peu moins. Il s’agit de la bibliothèque Reactive Extensions (Rx) qui permet de pratiquer la programmation réactive façon Microsoft. Vous pouvez la télécharger sur le site de DevLabs et elle existe pour les Frameworks .NET 3.5 et .NET 4.0 ainsi que pour Silverlight 3.

    Si vous n’avez jamais entendu parler de la programmation réactive je vais essayer de faire une petite introduction afin de mieux comprendre la suite de ce post.

     

    La programmation asynchrone est difficile

    Dans la mesure du possible les développeurs tentent d’éviter la programmation asynchrone pour ne pas être noyé dans la marré des callback et parce que nos programmes sont non déterministes. Cependant la programmation asynchrone est devenue incontournable dans les applications d’aujourd'hui. Les applications clientes ont besoin d’une interface graphique fluide, les développeurs de Silverlight n’ont pas vraiment le choix, les application connectées, l’utilisation des web services…tous ces aspects font que l’utilisation d’une programmation asynchrone devient une nécessité.

     

    Comment créer des applications asynchrones dont le code est plus facile à comprendre et plus maintenable ?

    C’est ici que rentre en jeu la bibliothèque System.Reactive.dll donc des extension réactives. Tout le monde connait l’utilisation de l’interface IEnumerable. Toutes les collections l’implémentent ce qui permet leur parcours dans une boucle foreach. Nous pouvons par exemple utiliser Linq afin de manipuler les séquences de IEnumerables comme dans l’exemple ci-dessous:

    1
    2
    int[] nombres = new int[] { 1, 11, 34, 2, 7, 98, 78, 4 }; 
    IEnumerable<int> nombresTries = from n in nombres orderby n select n;

    Enumérables sont donc des séquences de données que nous tirons de la source de données. Par contre quand les données ne sont pas tirées mais poussées, nous devons réagir d’une manière appropriée est c’est ça la Programmation Réactive.

     

    Programmation Réactive

    La programmation réactive est omniprésente. Nous utilisons la programmation réactive à chaque fois lorsqu’on enregistre un event handler pour un évènement ou lorsqu’on on spécifie une fonction de callback lors de la programmation asynchrone.

    Nous sont tous habitués à ce type de programmation réactive mais il y a une autre manière de le faire. Comment ? Qu’est-ce qu’il se passerait si chaque élément de données était passé en tant qu’un élément dans une séquence à une méthode appelée réactivement ?

     

    Observateur = Itérateur ?

    Les données qui sont passées aux event handlers peuvent être conceptualisées en tant que séquences de données qui sont poussées au lieu de tirer.

    A chaque fois lorsqu’un évènement est déclenché un élément de données est poussé et le plus souvent c’est le EventArgs. De même lors de la programmation asynchrone. Lorsqu’une méthode de callback est évoquée des données sont poussées, le résultat de l’opération asynchrone.

    Toutes les opérations que vous pouvez effectuer sur les séquences en tirant le données peuvent également être effectuées sur les séquences en poussant les données.

    Enumarateur = les données sont tirées de la source de données.

    Observateur = les données sont poussées de la source de données.

    Cela veut tout simplement dire et c’est la conclusion à laquelle sont arrivé les créateurs de la bibliothèque de la programmation réactive que les Design Patterns Observateur et Iterateur sont en fait les même patterns !

     

    Exemple Drag & Drop en WinForm

    Un petit exemple pour vous démontrer la puissance de la programmation réactive s’appuiera sur le Drag & Drop en WinForms (libre à vous de le faire en WPF ou Silverlight). Je suis en train de découvrir cette magnifique bibliothèque donc je ne vais pas vous parler des détails d’implémentation. C’est peut-être l’exemple le plus banal évoqué sur les blogs traitant de la programmation réactive. Il est possible d’y arriver par différents moyens mais étant donné que cette technique n’a pas encore été évoquée sur les blogs de code sources je me permets de le faire.

    Le but de cette exercice et de permettre à tous les contrôles sur un form windows d’être draggables. Par exemple avec le code suivant :

    1
    2
    3
    4
    foreach (Control control in this.Controls) 
    {
    control.AllowDragging();
    }

    Mais avant de le faire voyons comment nous pouvons le faire façon “OLD SCHOOL” donc sans les extensions réactives.

    Old School style

    Le code n’est pas le plus optimisé et il y a d’autre manière de le faire mais imaginons que nous voulons prendre un bouton “button1” et le placer n’importe où sur la form.

    dragDrop

    Les évènements qui nous intéresserons sont :

    button1_MouseDown : pour initier le dragging

    button1_MouseMove : pour récalculer la nouvelle position du bouton lorsque la souris se déplace sur la forme.

    button1_MouseUp : pour lâcher le bouton sur la forme et terminer le dragging.

    L’implémentation de ces événements peut ressembler à ceci :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    bool dragging = false; 
    int mousex;
    int mousey;

    private void button1_MouseDown(object sender, MouseEventArgs e)
    {
    if (e.Button == System.Windows.Forms.MouseButtons.Left)
    {
    dragging = true;
    mousex = -e.X;
    mousey = -e.Y;
    int clipleft = this.PointToClient(MousePosition).X - button1.Location.X;
    int cliptop = this.PointToClient(MousePosition).Y - button1.Location.Y;
    int clipwidth = this.ClientSize.Width - (button1.Width - clipleft);
    int clipheight = this.ClientSize.Height - (button1.Height - cliptop);
    Cursor.Clip = this.RectangleToScreen(new Rectangle(clipleft, cliptop, clipwidth, clipheight));

    button1.Invalidate();
    }
    }

    private void button1_MouseMove(object sender, MouseEventArgs e)
    {
    if (dragging)
    {
    var position = new Point();
    position = this.PointToClient(MousePosition);
    position.Offset(mousex, mousey);
    button1.Location = position;
    }
    }

    private void button1_MouseUp(object sender, MouseEventArgs e)
    {
    if (dragging)
    {
    dragging = false;
    Cursor.Clip = new Rectangle();
    button1.Invalidate();
    }
    }

    Voyons maintenant comment nous pouvons implémenter la même chose avec la programmation réactive.

    Reactive Programming Style

    Comment nous allons faire. Tout d’abord commençons à ajouter un référence vers la bibliothèque System.Reactive.dll et System.CoreEx.dll. Ensuite, les extensions Rx nous permettent de traiter les évènements .NET (et non seulement) comme une source de données que nous pouvons interroger avec une requête LINQ. Nous allons utiliser la méthode Observable.FromEvent.

    Tout d’abord occupons nous de la méthode MouseDown. Si nous nous rappelons ce que j’ai écrit plus haut dans la programmation réactive nous pouvons considérer l’évènement MousDown en tant qu’une séquence de EventArgs qui sont poussés à chaque fois qu’un évènement se produit. Donc, l’évènement MouseDown est représenté en tant qu’une collection de données où chaque donnée représente un click avec la souris. Voyons le code qui permet de nous convertir l’évènement MouseDown en cette collection de données. Le plus simple est de faire une méthode d’extension pour que cette évènement soit valable pour tout type de contrôle.

    1
    2
    3
    4
    5
    6
    7
    8
    public static IObservable<IEvent<MouseEventArgs>> DoMouseDown(this Control control) 
    {
    return Observable.FromEvent<MouseEventHandler, MouseEventArgs>(
    handler => new MouseEventHandler(handler),
    handler => control.MouseDown += handler,
    handler => control.MouseDown -= handler
    );
    }

    Grâce à cette implémentation nous pouvons d’une manière différente que juste un simple enregistrement à un évènement afficher un message box lorsqu’on clique sur le bouton :

    1
    button1.DoMouseDown().Subscribe(handler => MessageBox.Show("Click sur le bouton1 avec évènement MouseDown"));

    Vous me diriez ça sert à quoi vu que nous pouvons utiliser la manière standard de le faire ? Mais dites-moi, si je vous demande d’implémenter la même chose de la façon old school et afficher le MessageBox juste pour les 3 premiers click. Ce n’est plus si banal que cela. Nous devrions garder un compteur interne de clicks que nous devrions incrémenter lors de chaque click et de consulter lorsqu’un évènement est évoqué, se désabonner…enfin cela se complique très vite ! Voyons comment c’est facile avec la programmation réactive:

    1
    button1.DoMouseDown().Take(3).Subscribe(handler => MessageBox.Show("Click sur le bouton1 avec évènement MouseDown"));

    Après 3 click plus aucun évènement n’est lancé. Cooooooool !

    Passons maintenant à notre implémentation du Drag façon réactive ! Comment le Drag & Drop se passe en réalité ?

    1. Tout d’abord il y a l’évènement MouseDown. Nous sommes intéressé que par le click avec le bouton gauche de la souris.
    2. Ensuite il y a le MouseMove qui se passe lorsqu’on bouge la souris.
    3. Et à la fin il y a MouseUp lorsqu’on relâche la souris.

    Définissons d’abord les méthodes d’extension pour les évènements MouseMove et MouseUp:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public static IObservable<IEvent<MouseEventArgs>> DoMouseMove(this Control control) 
    {
    return Observable.FromEvent<MouseEventHandler, MouseEventArgs>(
    handler => new MouseEventHandler(handler),
    handler => control.MouseMove += handler,
    handler => control.MouseMove -= handler
    );
    }

    public static IObservable<IEvent<MouseEventArgs>> DoMouseUp(this Control control)
    {
    return Observable.FromEvent<MouseEventHandler, MouseEventArgs>(
    handler => new MouseEventHandler(handler),
    handler => control.MouseUp += handler,
    handler => control.MouseUp -= handler
    );
    }

    Ensuite, définissons une classe qui contiendra les informations que nous voulons passer à ces évènements. Ce qui nous intéresse c’est le contrôle que nous sommes en trains de bouger, la position initiale de la souris par rapport à la position du contrôle et la position actuelle de la souris. Nous pouvons l’implémenter de la manière suivante :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class DragInformation 
    {
    public Control DragableControl { get; private set; }
    public Size InitialLocation { get; private set; }
    public Point NewLocation { get; private set; }

    public DragInformation(Control dragableControl, Size initialLocation, Point newLocation)
    {
    DragableControl = dragableControl;
    InitialLocation = initialLocation;
    NewLocation = newLocation;
    }
    }

    De cette manière avec les extensions réactives nous pouvons COMPOSER les évènements et les implémenter avec une expression LINQ d’après le scénario écrit ci-dessus (1 – MouseDown, 2 – MouseMove, 3 – MouseUp)…

    1
    2
    3
    4
    5
    6
    7
    8
    public static IObservable<DragInformation> DoDrag(this Control control) 
    {
    return from mouseDown in control.DoMouseDown()
    where mouseDown.EventArgs.Button == MouseButtons.Left
    from mouseMove in control.DoMouseMove().Until(control.DoMouseUp())
    select new DragInformation(control, new Size(mouseDown.EventArgs.Location),
    Point.Add(control.Location, new Size(mouseMove.EventArgs.Location)));
    }

    Ensuite il faut ajouter une méthode d’extension qui ajoutera à chaque contrôle la capacité d’être dragable :

    1
    2
    3
    4
    5
    6
    7
    public static void AllowDragging(this Control control) 
    {
    control.DoDrag().Subscribe(info =>
    {
    info.DragableControl.Location = Point.Subtract(info.NewLocation, info.InitialLocation);
    });
    }

    De cette manière nous pouvons simplement activer les drag & drop pour tous les contrôles sur la forme:

    1
    2
    foreach (Control control in this.Controls) 
    control.AllowDragging();

    Conclusion

    Nous avons juste vu à peine toutes les possibilités qui offre cette bibliothèque. Je vais certainement consacrer quelques posts à son sujet. Bien que cette bibliothèque ouvre des horizons le deboguage des applications semble un peu plus compliqué. C’est également un sujet intéressant à creuser.

    A bientôt.

    Une fonctionnalité intéressante que j’ai découvert lors du BizSpark présenté par Scott Guthrie, est le mode “consume-first” de IntelliSense qui permet très aisément de pratiquer le TDD. Pour rappel, le TDD peut être résumé par la phrase From Red To Green et impose les étapes suivantes :

    1. Red : crée un test et fais le échouer.
    2. Green : fais passer le test par tous les moyens en écrivant du code de production sans se trop soucier de la forme.
    3. Refactor : Modifie le code pour enlever les duplications de code dans le projet, améliorer le design en s’assurant que tous les tests passent toujours.

    Si on suit la logique du TDD on doit d’abord écrire le test avant d’écrire le code de production. Pour faire simple, admettons que dans notre application ASP.NET MVC nous n’avons pas le “HomeController” (ni le test unitaire associé) et nous voulons le créer. Donc d’abord il faut créer le test unitaire de notre contrôleur avant de créer le contrôleur lui même. Je crée donc ma classe “HomeControllerTests” dans mon projet des tests :

    1
    2
    3
    4
    5
    6
    7
    8
    [TestClass] 
    public class HomeControllerTests
    {
    [TestMethod]
    public void Index()
    {
    }
    }

    Rien de spécial. Maintenant il faut que j’écrive mon test pour le contrôleur qui n’existe pas encore et là j’ai un petit souci. Lorsque j’essaie d’écrire du code contre une classe qui n’existe pas encore, forcément IntelliSense de Visual Studio me propose le choix qu’il connaît, comme ici, c’est la classe de test elle-même:

    TDDVS2010

    Quand vous effectuez une tabulation ou cliquez sur l’espace il vous insérera la classe “HomeControllerTests” ce qui n’est pas le but. Il existe cependant un moyen de dire à IntelliSense que le code que nous allons écrire ne se réfère pas à une classe qui existe déjà mais que nous allons créer plus tard.

    Il s’agit du mode “consume-first” que vous activez avec la combinaison des touches Ctrl+Alt+Espace. Notez la différence:

    TDDVS2010_2

    Visuellement il y a une ligne supplémentaire qui apparaît dans IntelliSense. Maintenant quand vous écrivez votre code, l'IntelliSense ne vous “force” pas à choisir parmi les sélections disponibles. Une fois les tests terminés:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    [TestMethod] 
    public void Index()
    {
    // Arrange
    HomeController controller = new HomeController();

    // Act
    ViewResult result = controller.Index() as ViewResult;

    // Assert
    ViewDataDictionary viewData = result.ViewData;
    Assert.AreEqual("Welcome to ASP.NET MVC!", viewData["Message"]);
    }

    Mais ce n’est pas fini ! Vous pouvez facilement générer votre contrôleur à partir de votre code de test :) Pour cela placez vous sur la ligne du “HomeController” et faites Ctrl+. (point). Vous verrez un petit menu qui s’affiche:

    TDDVS2010_3

    Je choisi “Generate new type…” pour pouvoir ajouter ma nouvelle classe dans mon application ASP.NET MVC et non au même endroit que ma classe de tests (comportement par défaut si vous cliquez sur la première option).

    TDDVS2010_4

    Notez que vous pouvez choisir l’endroit où vous voulez que votre classe soit créée. Maintenant je peux aller compléter ma classe juste pour que la solution compile. Quand je lance mon test unitaire je vois bien que la méthode ne marche pas bien:

    TDDVS2010_6

    Je retourne donc dans ma classe et y ajoute le code qui est censé tester mon test unitaire. Si tout va comme prévu j’obtiens le résultat suivant:

    TDDVS2010_5

    Conclusion

    Comme vous pouvez le constater ceci n’est pas une grande révolution mais cependant je trouve que ça vaut le coup d’être mentionné. Je trouve bien l’idée que Microsoft essaie d’aller dans le bon sens et inclus dans leur produit des petites fonctionnalités comme celle-ci. Moi, qui suis sensible à tout ça, cela me plaît beaucoup ;)

    A bientôt.

    Depuis un certain temps je développe à fond avec le framework ASP.NET MVC. Il existe de nombreux livres et blogs à son sujet, le plus connu étant celui de Scott’Gu et son application NerdDinner. J’ai également envie de consacrer à ce Framework quelques posts en traitant d’autres sujets à côté comme par exemple : les tests unitaires que je présente dans ce post, l’architecture générale, plug-ins avec MEF, WF, etc. Pour le moment je vais faire une courte introduction aux tests unitaires dans ASP.NET MVC 2 avec NBehave, d’autres sujets seront introduits au fur et à mesure. J’essaierai de me concentrer si le sujet le permet, sur les nouveautés qui sont introduits avec la nouvelle version du framework ASP.NET MVC 2 mais ce n’est pas mon but ultime. Dans ce poste j’utilise la version MVC 2 mais cela s’applique également à la première version de MVC.

    Pour rappel, un des points le plus importants du succès du Framework ASP.NET MVC est sa testabilité. Il a été construit pour simplifier les parties complexes des Web Forms tout en gardant la puissance et la flexibilité de ASP.NET classique. En bref ASP.NET MVC est fourni pour supporter les applications ASP.NET en utilisant le Model View Controller pattern de présentation. Le code du contrôleur est contenu dans une classe faiblement couplée aux dépendances alors que les pages web se sont transformées en des simples vues qui ne sont rien d’autre que les Template HTML rempli avec des objets passés par le contrôleur. Il n’y a plus de cycle de vie des évènements Postback donc le Viewstate n'est plus nécessaire.

    Quand vous créez un nouveau projet ASP.NET MVC 2 sous Visual Studio 2010 (ou 2008), un assistant vous demande si vous voulez créer un projets de tests. Par défaut l’assistant vous propose d’utiliser “Visual Studio Unit Test” framework. Vous pouvez cependant ajouter d’autres framework de test comme par exemple NUnit ou MbUnit. (attention les version release de ces frameworks de tests ne sont pas encore disponible pour le CLR 4). Pour en savoir plus vous pouvez lire le poste qui décrit cette opération pour la version 2008 de Visual Studio: http://blogs.msdn.com/webdevtools/archive/2008/02/18/asp-net-mvc-test-framework-integration.aspx. Je n’ai pas testé par contre si cela fonctionne sous Visual Studio 2010. Je vous dirai juste que si vous voulez faire le test, partez des sources de NUnit et compilez le en version 4 de framework. Si quelqu’un fait le test je serais content d’avoir un retour pour mettre à jour ce billet.

    Pour ma part j’ai toujours été habitué à utiliser NUnit et RhinoMocks pour réaliser mes tests unitaires. D’ailleurs j’utilise à fond le projet MvcContrib qui fourni pas mal de fonctionnalités supplémentaires comme par exemple:

    • Injection de dépendances
    • ViewData forement  typé
    • Validation helpers
    • nouveaux ActionResults (BinaryResult et XmlResult)
    • nouvaux filtrès
    • et beaucoup plus. Visitez la page de MvcContrib.

    Mais puisqu’on parle des tests vous trouverez également une bibliothèque qui s’appelle le TestHelper qui vous fourni une Factory pour les contrôleurs ce qui permet de les initialiser correctement. Vous y trouverez par exemple :

    • HttpContext
    • HttpRequest
    • HttpResponse
    • HttpSession
    • Form
    • TempData
    • QueryString
    • ApplicationPath
    • PathInfo

    Mais ne nous égarons pas. Nous devons réaliser les tests avec la librairie NBehave.

    NBehave ? Qu’est-ce que c’est ?

    Avant de parler de NBehave nous allons parler de BDD c’est-à-dire de Behaviour Driven Development. Je suis désolé si ma définition ne plaît pas aux puristes du domaine mais on peut parler du BDD en tant que “l’évolution” du bon vieux TDD (Test Driven Development). BDD englobe le TDD et le DDD (désolé pour tous ces acronymes :) ) donc Domain Driven Design. En bref, le but du BDD est de concentrer les efforts de développement sur l’obtention de la valeur ajoutée vérifiable pour le business. Le langage commun qui est mis en place permet de diminuer le gap entre le Business et la Technologie. Nous n’allons pas rentrer dans les détails du BDD mais si vous voulez en savoir plus je vous invite à consulter les sites suivants:

    Concrètement nous pouvons résumer le processus BDD de la manière suivante:

    Un utilisateur “business” (donc celui qui connaît le métier) travaille avec un business analyste afin d’identifier les besoins du métier (business). Ce travail est exprimé en tant qu’une Story (histoire) conforme à ce modèle:

    As a Role

    I Request a Feature

    To gain a Benefit

     

    Je ne sais pas si la traduction est parfaite mais en gros cela veut dire:

    En tant que Rôle

    Je demande une Fonctionnalité

    Afin d’obtenir un Bénéfice

     

    La personne qui tient le Rôle est la personne qui aura le Bénéfice de la Fonctionnalité demandée.

    Vous-vous demandez certainement quel rapport a tout cela avec les tests et le développement ? Personnellement je me suis posé la même question et bien, vu que je ne suis pas l’expert du BDD (c’est une découverte) en cherchant un peu je suis tombé sur l’explication suivante :

    “BDD défini les interactions et les rôles entre les objets. Cela encourage les développeurs à penser au Comportement d’un composant qu’ils développent, aux Rôles et aux Responsabilités des autres objets avec lesquels il interagi.”

     
    The Story (une histoire)

    Tout bon développement BDD doit commencer par une Story donc une histoire. Pour cela nous allons utiliser la classe du même nom qui sert à décrire les scénarios que nous allons tester. Je me conforme au template que je défini ci-dessus:

    En tant qu’un utilisateur

    Je veux accéder à la page d’accueil

    Afin de voir le message d’accueil ‘Welcome to ASP.NET MVC!’

     
    Le Scénario

    Une Story est composée d’un ou plusieurs scénarios. Les scénarios s’expriment à l’aide des mots Given, When, Than. Nous allons écrire deux scénarios, une pour l’action Index() et l’autre pour About() toutes les deux définies dans le contrôleur HomeController.

    Given (étant donné) que mon contrôleur est instancié

    When (quand) je navigue à la page d’accueil

    Than (alors) ma page d’accueille est instanciée est affichée

     

    Passons maintenant à un peut de pratique.

    Assez de parler ! Un petit exemple s’impose !

    Pour cela nous allons créer une simple “ASP.NET MVC 2 Web Application”. Je pense que pour comprendre NBehave nous n’avons pas besoin de plus. On va commencer avec HomeController parce qu’il est plus facile que AccountController. Nous allons l’exploiter dans un deuxième exemple.

    Préparer l’environnement

    1. Dans Visual Studio 2010, créez-votre “ASP.NET MVC 2 Web Application”. Quand une fenêtre vous demandera de créer un projet de tests unitaires, cliquez sur “Non”. Nous allons utiliser à la place NUnit avec NBehave (désolé pour les fans de MSTest).

    2. Ajoutez ensuite un nouveau projet “Class Library” à votre solution que vous allez appeler <nom de mon web application>.Tests. Comme vous-vous en doutez cela sera notre projet de tests. Créez ensuite votre classe de tests “HomeControllerTests” dans le dossier “Controllers” que vous créez également à la racine de ce projet.

    3. Dans votre projet de tests ajoutez une référence à notre application web.

    4. Vous devez également télécharger les sources de NUnit http://www.nunit.org/index.php?p=download et NBehave http://code.google.com/p/nbehave/source/checkout. Ces bibliothèques ne sont pas encore prête pour travailler avec le Framework 4.0 donc vous devez d’abord ouvrir les sources dans dans Visual Studio 2010, convertir les solutions et générer les assemblies pour le Framework 4.0. 

    4. Ajoutez ensuite les références suivantes:

    1
    2
    3
    using NBehave.Narrator.Framework; 
    using NBehave.Spec.NUnit;
    using NUnit.Framework;

    La première référence NBehave.Narrator.Framework importe l’API d’un “narrateur”. C’est une interface mimique un narrateur qui conte une histoire (un scénario). C’est cette classe qui permet d’utiliser le code pour raconter une histoire comme par exemple “En tant qu’un utilisateur, j’accède à la page d’accueil et je vois le message ‘Welcome to ASP.NET MVC!’”.

    La deuxième référence NBehave.Spec.NUnit contient la définition de la classe SpecBase de laquelle dérive notre classe de test. C’est aussi un pont entre NUnit et NBehave. Je vais expliquer l’intérêt de la classe SpecBase dans un article à part mais pour le moment tout ce que vous avez à savoir ce que cette classe permet d’auto mocker avec RhinoMocks les dépendances.

    Vous devez ajouter une troisième référence vers NBehave.Spec.Framewrok.

    Attention: la technique montré ici est “old style”. C’est-à-dire, maintenant pour déclarer une Story il est conseillé de passer par les attributs des classes et des méthodes, c’est une manière déclarative. Pour la simplicité de l’exercice j’utilise la vieille technique à partir du code d’une manière impérative.

    5. Décorer vos méthodes de test. Comme vous faites vos tests unitaires vous utilisez l’attribut [Test] pour décorer vos méthodes, mais pour les développeurs qui font du BDD ces attributs sont redéfinis comme ceci:

    1
    2
    3
    4
    5
    6
    using Specification = NUnit.Framework.TestAttribute; 
    using That = NUnit.Framework.TestAttribute;
    using Describe = NUnit.Framework.CategoryAttribute;
    using For = NUnit.Framework.CategoryAttribute;
    using Wrote = NUnit.Framework.DescriptionAttribute;
    using Should = NUnit.Framework.DescriptionAttribute;
    La seule raison de redéfinir ces attributs est pour augmenter le lisibilité des tests.

    6. Définissons maintenant notre Story. Ceci n’est pas un test unitaire mais uniquement une description de User Story. Pour cela nous allons utiliser fluent interface de NBehave, c’est-à-dire une interface ou chaque méthode retourne this, ce qui permet de faire le chaînage des méthodes. Comme je l’ai signalé c’est une façon impérative de travaillé alors NBehave nous demande de passer à la manière déclarative avec des attributs.

           

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    private HomeController controller; 
    private Story story;

    [Story, That, Should("Afficher la page d'accueil avec le message 'Welcome to ASP.NET MVC!'")]
    public override void MainSetup()
    {
    base.MainSetup();

    story = new Story("Page d'accueil");
    controller = new HomeController();

    story.AsA("utilisateur")
    .IWant("accéder à la page d'accueil")
    .SoThat("je vois le message ‘Welcome to ASP.NET MVC!’");
    }
    7. Maintenant que nous avons notre Story, définissons un Scenario. Le scénario sera notre premier test unitaire. Le scénario décrit une chose possible qui peut arriver comme par exemple l’affichage de notre page d’accueil. Dans la logique de BDD/TDD d’abord nous devons écrire le test avant le code de production. Ensuite, le test écrit doit échouer afin de s’assurer que notre code que nous allons tester ne marche pas par hasard, etc. Pour la simplicité de cette exemple nous n’allons pas rentrer dans le respect des étapes du TDD et nous allons passer directement au petit test qui s’assurer que notre message est bien affiché sur la page d’accueil

           

    1
    2
    3
    4
    5
    6
    7
    8
    9
    [@Specification] 
    public void Index()
    {
    ViewResult result = null;
    story.WithScenario("Ma page d'accueil")
    .Given("Mon contrôleur est prêt", () => controller.ShouldNotBeNull())
    .When("Quand un utilisateur navigue à la vue Index", () => result = controller.Index() as ViewResult)
    .Then("On voit le message de bienvenue", () => "Welcome to ASP.NET MVC!".ShouldEqual<string>(result.ViewData["Message"].ToString()));
    }

    Tout d’abord nous commençons par le WithScenario pour indiquer à l’outil de test avec quel scénario nous allons travailler.

    • Ensuite nous utilisons Given() comme pré condition à notre test. Ici notre contrôleur n’est doit pas être null. Une Action est également passée en paramètre (controller.ShouldNotBeNull()) qui fait ce que nous décrivant “Mon contrôleur est prêt”.
    • When() permet de décrire l’action que nous testons
    • Then() permet de vérifier que tout s’est passé correctement.

    Quand on exécute notre test dans NUnit nous voyons que tout se passe bien:

    NunitOK

    Pour montrer que le test marche j’introduit volontairement une faute dans la dernière ligne de notre test :

    1
    .Then("On voit le message de bienvenue", () => "Welcome to ASPsss.NET MVC!".ShouldEqual<string>(result.ViewData["Message"].ToString()));

    Si on relance NUnit alors le test ne passe plus:

    NUnitKO

    Nous pouvons ajouter le deuxième test pour l’action About:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    [@Specification] 
    public void About()
    {
    ViewResult result = null;
    story.WithScenario("Ma page d'accueil")
    .Given("Mon contrôleur est prêt", () => controller.ShouldNotBeNull())
    .When("Quand un utilisateur navigue à la vue About", () => result = controller.About() as ViewResult)
    .Then("La vue est affichée", () => result.ShouldNotBeNull());
    }

    Conclusion

    Comme vous pouvez le constater l’utilisation de NBehave n’ajouter rien de nouveau par rapport à ce qui existe déjà. C’est une autre approche pour écrire de tests unitaires. L’avantage est qu’elle est plus concise que la plus part d’autre frameworks et plus déclarative donc forcément plus facile à comprendre sans analyser forcément le code. Quand vous passez à l’utilisation des attributs pour décrire vos scénario le déclaratif est poussé encore à un autre niveau. Il est plus facile d’implémenter les Use Cases dans vos tests et de conformer votre application à des vrai besoins clients. C’est approche est d’autant plus vraie que si NBehave est appliqué aux tests du la couche métier. Un autre point important concerne ASP.NET MVC qui est facilement testable à l’aide de n’importe quel framework de tests. Dans un prochain article je vais présenter quelques astuces pour tester les application MVC à l’aide de MSTest.

    A bientôt.

    Enfin un outil qui peut-être facilitera la création de mappings entre la base de données et les entités de votre modèle métier. De plus c’est un outil graphique ce qui facilite grandement la tâche. Il s’agit de Visual NHibernate qui est en version beta et que vous pouvez télécharger ici : http://sharptoolbox.com/tools/visual-nhibernate.

    Le fan que je suis de NHibernate, je me suis empressé de l’installer pour vérifier les fonctionnalités qu’il propose. Sur le papier il y en a qui m’enchantent vraiment comme par exemple:

    • permet de démarrer d’une base existante, d’un code source existant pour un projet NHibernate, ou d’une nouvelle solution.
    • supporte TOUS les scénario de mapping NHibernate c’est-à-dire une entité à une table de la base de données, une entité à plusieurs tables en base de données, une table à plusieurs entités.
    • mappings unidirectionnels et bidirectionnels. Les one-to-one, one-to-many, many-to-many.
    • L’héritage est également géré. Supporte toutes les stratégie de NHibernate: table par classe concrète, table pour une hiérarchie des classes, table par sous-classe.
    • Etc.

    Je viens de l’installer, le produit présente encore quelques bugs non gênants. Je n’ai pas pu tester les scénario avancés car le but était juste de voir le produit. J’ai créé un nouveau projet sur la base Northwind et les mappings ont été générés :)

    En voici un aperçu:

    VisualNHivernate

    Ensuite je clique sur WriteFiles et tout est généré:

    VisualNHibernate2

    J’ai vu qu’on pouvait facilement configurer les entités et les liens entre les entités tout simplement en sélectionnant les partie qui nous intéresse directement sur le diagramme. Donc on peut facilement configurer le Lazy loading etc.

    Je vais m’amuser un peu plus avec ce produit ce weekend pour voir comment sont supportés les scénarios un peu plus avancés.

    En tout cas le produit a l’air d’être prometteur :)

    A bientôt.

    Etant dans la réflexion générale sur l’intégration continue en général, j’ai exploré quelques pistes dans le résumé est ci-dessous. J’espère que ceux qui ne connaissent pas ce processus se rendrons mieux compte de combien il est important dans la vie des nos projets et applications. (j’ai pas mis d’images, désolé :))

    Dans le milieu professionnel des entreprises, notre job des développeurs, est d’écrire des applications de meilleure qualité en un minimum de temps. Avec le temps les application deviennent de plus en plus complexes et difficiles à gérer. On nous dit que l’intégration continue (CI) permet d’augmenter la productivité de nos applications en automatisant certaines parties de notre travail. J’ai passé pas mal de temps en cherchant les informations concernant l’intégration continue donc j’ai décidé de partager mes recherches avec vous, afin de vous donner une base de départ pour vos propres exploration si le sujet vous intéresse. Avant de passer plus loin posons nous les questions suivantes :

    • Quels problèmes permet de résoudre l’intégration continue ?
    • Quels sont les outils à mettre en place  et est-ce que ça coûte cher ?
    • Quelles sont les tâches pouvant être automatisée dans notre vie de développeurs ?

    Avant de répondre à toutes ces questions nous devons nous poser la question :

    Qu’est-ce que c’est l’intégration continue ?

    Martin Fowler écrit sur le sujet : http://martinfowler.com/articles/continuousIntegration.html ce que nous pouvons résumer de la manière suivante:

    “L’intégration continue est un processus automatisé qui génère, teste, déploie et analyse une application afin de s’assurer qu’elle fonctionne correctement, suit les best practices et s’installe chez le client. Le processus s’exécute avec chaque changement de code source et fourni un retour immédiat aux développeurs.”

    Quand on lit cette définition on peut vite avoir peur au premier abord. Ce qu’il faut savoir ce que quand vous mettez l’intégration continue en place dans votre équipe de développement, n’essayez pas de suite de mettre tout en ouvre. Nous y viendrons tout à l’heure lorsqu’on essayera de définir un plan.

    Maintenant que nous avons défini ce que c’est une intégration continue, voyons quels problèmes l’intégration continue permet de régler.

    Quels problèmes permet de résoudre l’intégration continue ?

    Quand vous développez en équipe, il arrive très souvent que votre partie de code marche parfaitement chez vous et lorsque vous intégrez cette pièce de code avec le reste de l’application vous découvre les effets de bord. Surtout si entre temps un autre développeur a mis à jour sa partie de code. De même, combien de fois il arrive que lorsqu’une application est livrée chez le client, des problèmes apparaissent qui ne sont pas reproductibles en local. Tout ces problèmes peuvent “en partie” être gommés en adaptant l’intégration continue.

    Le risque

    L’intégration continue permet de réduire le risque. Le risque n’est pas bon dans le développement des logiciels. Tout le monde connaît les problèmes de planning, les découvertes de jours manquant presque à la dernière minute. Je dirai que c’est le plus grand risque que nous pouvons avoir, c’est le planning qui bouge constamment. Le processus de CI a un mécanisme de feedback et toute équipe connaît le statut du dernier build ce qui permet entre autres de savoir si le projet est sur les railles. De même, le feedback permet d’identifier les BUGS au plus tôt, ce qui est un autre risque important. Combien moins cher coûte la correction de bug tout de suite grâce au mécanisme de feedback de CI que de le corriger une fois chez le client ? Les impacts peuvent être très importants.

    La qualité

    Un autre point important est la qualité. Le processus de CI permet de l’augmenter grâce à l’analyse du code et aux test unitaires. Personne n’a jamais testé manuellement tous le chemins possible d’exécution du code dans l’application, tous les if/else etc. les tests unitaires et l’analyse de code automatique permet de le faire pour vous dans le processus de CI.

    Maintenant que nous savons comment peut nous aider l’intégration continue, voyons les outils dont se sert l’intégration continue et répondre par la même à la première question.

    Quels sont les outils à mettre en place  et est-ce que ça coûte cher ?

    Il y a heureusement pour nous plein d’outils qui peuvent nous servir pour mettre en place le processus de CI. Il y a des outils, dont l’administration et la configuration est relativement aisée, qui offrent beaucoup de fonctionnalités et par conséquent sont payant. Nous pouvons citer par exemple Team Foundation Server de Microsoft. Je n’ai pas utilisé exprès le mot “cher” car cette notion est relative et souvent quand le processus de CI est mis en place et rôdé le retour sur l’investissement est perceptible. Il y a aussi des outils OpenSource gratuit qui peut-être ne sont pas si aboutis dans l’administration et la configuration que les versions payantes mais qui cependant permettent également à mettre en place un processus CI robuste. Voyons un peu ce dont on aura besoin (la liste n’est pas exhaustive, vous pouvez trouver d’autres outils en cherchant bien sur le web):

    • Contrôleur de code source (avec le versionning):

    - Subversion (http://subversion.tigris.org/) est gratuit et centralisé. Vous aurez également besoin d'un outil comme Tortoise SVN (http://tortoisesvn.tigris.org/) ou AnkhSVN (http://ankhsvn.open.collab.net/) pour travailler avec ce gestionnaire des sources.

    - Git (http://git-scm.com/) est gratuit mais décentralisé

    - TFS Version Control (avec Team Foundation Server) est payant.

    - SourceSafe est payant également.

    • Un outil pour gérer le processus d’intégration continue (vérifie les changements dans contrôleur de sources, déclenche les builds, fourni le feedback à l’équipe de développement).
      • envoie un feedback sur l’état des opérations

     - Visual Studio Team Suite et Team Foundation Server qui est payant.

    - CruiseControl.NET (http://cruisecontrol.sourceforge.net/) qui est gratuit.

    - TeamCity de JetBrains (http://www.jetbrains.com/teamcity/) qui est gratuit pour les petites équipes et les petits projet.

    • Un outil de build qui compile le code le feedback avec le résultat de la compilation est envoyé par le serveur qui exécute le processus CI.

    - MSBuild est gratuit si vous voulez l’utiliser sans le TFS.

    - NAnt (http://nant.sourceforge.net/) qui est gratuit également.

    • Un outil pour les tests unitaires.

    - MSTest qui se trouve dans Visual Studio (Team Suite et Test, je crois)

    - NUnit (http://www.nunit.org/index.php) gratuit et un des plus populaires.

    - MbUnit (http://www.mbunit.com/) qui est également gratuit et aussi populaire.

    - xUnit.NET (http://www.codeplex.com/xunit) est également gratuit.

    • Un outil pour l’analyse et la métrique du code. Ces outils vous permettent de mesurer divers point comme par exemple le respect des règles de nommage par tous les membres de l’équipe.

    - FxCop de Microsoft (http://www.microsoft.com/downloads/details.aspx?familyid=9AEAA970-F281-4FB0-ABA1-D59D7ED09772&displaylang=en).

    - StyleCop de Microsoft également (http://code.msdn.microsoft.com/sourceanalysis/Release/ProjectReleases.aspx?ReleaseId=1425) qui est gratuit et qui permet de vérifier le code s’il respecte les best practices.

    - NCover (http://www.ncover.com/) qui n’est pas gratuit et qui permet de tester si tous les chemin d’exécution de code ont été testés. L'application doit être en exécution pour cela.

    - Gendarme (http://www.mono-project.com/Gendarme) qui a l'air pas mal en analyse statique.

    • Un outil pour exécuter d’autres tests (acceptance, intégration) que les tests unitaires.

    - Selenium (http://seleniumhq.org/) pour tester les application web.

    - NUnitForms (http://nunitforms.sourceforge.net/) est une extension de NUnit pour tester les WinForms.

    - FitNess (http://fitnesse.org/) qui est gratuit et permet d’effectuer les test d’acceptance.

    - WatiN (http://watin.sourceforge.net/) qui est également gratuit et qui petmet de tester votre interface web.

    • Un outil pour la création de la documentation à partir des commentaires XML contenus dans le code.

    - SandCastle (http://www.codeplex.com/Sandcastle) qui est gratuit.

    - NDoc (http://ndoc.sourceforge.net/).

    - DOxygen (http://www.stack.nl/~dimitri/doxygen/)

    • Un outil pour le déploiement automatique.

    - ClickOnce

    - Visual Studio Installer

    - WiX (http://wix.sourceforge.net/).

    • D’autres outils qui peuvent servir.

    - ReSharper (http://www.jetbrains.com/resharper/index.html) un add-in payant pour Visual studio qui aide au développement et refactoring. Personnellement je ne peux pas m’en passer.

    - NDepend (http://www.ndepend.com/) est également payant me permet de gérer et d’analyser le code .NET en spécifiant des règles. Une petite introduction est ici : http://blogs.developpeur.org/tja/archive/2009/10/07/introduction-a-la-d-couverte-de-ndepend.aspx.

    Conclusion

    Après une petite introduction à l’intégration continue, on devrait y voir plus clair les intérêts de ce processus dans le développement de tous les jours. Vous savez maintenant comment l’aborder et les outils qui peuvent vous servir. Pour ceux qui sont arrivé jusqu’à la fin de cet article (que j’ai remercie car il est un peu long) et qui se rendent compte que je n’ai pas encore traité une question “Quelles sont les tâches pouvant être automatisée dans notre vie de développeurs ?”, je dirai que je vais la traiter dans des prochains posts, comme d’ailleurs d’autres sujets qui sont liés à ça. Si cela vous intéresse vraiment on peut réfléchir sur une mise en place d’une intégration continue à titre d’exemple avec les outils Microsoft ou Open Source. Cela sera également un exercice pour moi, pour progresser dans ce domaine. En tout cas merci pour votre attention :)

    A bientôt :)

    Si vous vous demandez quel Framework MVVM utiliser, alors peut-être la liste sur laquelle je suis tombé par hasard pourrait vous aider à faire votre choix.

    http://www.japf.fr/silverlight/mvvm/index.html

    A bientôt;

    Posté le lundi 30 novembre 2009 21:32 par tja | 3 commentaire(s)
    Classé sous :

    Dans mon post précédent j’ai parlé des régions de Visual Studio. Pour rappel vous pouvez y jeter un coup d’œil:

    Cette fois ci je vais parler de quelque chose de très simple mais qui s’avère dans la pratique mal compris par les développeurs débutants. Quand on parle du passage des paramètres par valeur, tout le monde sait exactement comment ça fonctionne et personne n’a de difficultés pour définir ce comportement. En fait je voudrais plus particulièrement donner mon point de vue concernant le passage par référence aussi bien pour les types de valeur que pour les types de référence. En parlant aujourd’hui avec les développeurs débutants, je me suis aperçu qu’il y a des interprétations différentes concernant cette notion. Chacun connaît le comportement actuel de C# mais il leur est difficile de définir exactement ce que le passage par référence veut dire. Ceci dit cela ne les empêche pas de bien développer mais une petite explication s’impose.

    Le sujet paraît simple mais qu’en-est-il du réel comportement de C# ?

    Types valeurs

    Regardons d’abord un petit exemple que voici :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    static void Main(string[] args)
        {
          int index1 = 0;
          RaiseIndex(index1);
          Console.WriteLine("Valeur de l'index 1 : {0}", index1);

          int index2 = 0;
          RaiseIndex(ref index2);
          Console.WriteLine("Valeur de l'index 2 : {0}", index2);


        }

        private static void RaiseIndex(int index)
        {
          index++;
        }

        private static void RaiseIndex(ref int index)
        {
          index++;
        }

    Comme vous-vous en doutez à l’exécution de ce code vous obtenez : “0” pour index1 et “1” pour index2.

    Le premier appel ne passe pas index1 par référence mais par valeur. C’est-à-dire une copie de la valeur de la variable index1 est faite est passé à la méthode RaiseIndex. Les modifications faites à l’intérieur de la méthode sont visible qu’à l’intérieur de cette méthode. Une valeur sur la pile a été copiée vers un autre emplacement sur la pile.

    Dans le deuxième cas nous passons la valeur par référence. C’est-à-dire qu’aucune copie n’est faite et les modifications réalisées à l’intérieur de la méthode RaiseIndex sont visibles à l’extérieur.

    Types référence

    Le comportement est exactement le même lorsqu’il s’agit du passage des types référence bien que dans un premier temps il paraît différent. Regardons l’exemple suivant :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    public class Car 
    {
    public Car(int speed)
    {
    Speed = speed;
    }

    public int Speed { get; set; }
    }

    public class Helper
    {
    public static void IncreaseSpeed(Car car, int speed)
    {
    car.Speed += speed;
    }

    public static void IncreaseSpeed(ref Car car, int speed)
    {
    car.Speed += speed;
    }
    }

    static void Main(string[] args)
    {
    var car1 = new Car(100);
    Helper.IncreaseSpeed(car1, 20);
    Console.WriteLine(car1.Speed);

    var car2 = new Car(100);
    Helper.IncreaseSpeed(ref car2, 20);
    Console.WriteLine(car2.Speed);

    Console.ReadKey();
    }

    Comme vous le voyez le code est très simple. Après l’exécution de ce code comme vous-vous en doutez le résultat est 120 pour les deux cas. Au premier coup d’œil cela semble différent de ce qu’on a vu avec le type valeur. Ce qui se passe actuellement c’est lorsqu’on passe un type référence par valeur, une copie de la valeur est effectivement passée mais ce n’est pas la copie de la valeur stocké sur le tas mais la copie du pointeur puisque les types référence sont référencées par une adresse unique sur la tas en mémoire appelé le pointeur. Et puisque le pointeur et la copie du pointeur pointent vers le même emplacement mémoire sur le tas, le changement effectué par l’un est immédiatement répercuté sur l’autre.

    Quand on passe un type référence par référence, vous passez le pointeur actuel au lieu de la copie du pointeur. Cela nous amène à la question.

    Quand est-ce qu’on a besoin de passer un type référence par référence ?

    On le fait lorsqu’on veut modifier le pointeur lui-même. Cela peut avoir des effets de bords, donc finalement c’est une bonne chose que les fonctions doivent spécifier avec le mot clé “ref” leurs intentions, qu’elle souhaitent recevoir un paramètre par référence. Considérons l’exemple suivant:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    public class Car
      {
        public Car(int speed)
        {
          Speed = speed;
        }

        public int Speed { get; set; }
      }

      public class Helper
      {
        public static void Destroy(Car car)
        {
          car = null;
        }

        public static void Destroy(ref Car car)
        {
          car = null;
        }
      }

    static void Main(string[] args)
        {
          var car1 = new Car(100);
          Helper.Destroy(car1);
          Console.WriteLine(car1.Speed);

          var car2 = new Car(100);
          Helper.Destroy(ref car2);
          Console.WriteLine(car2.Speed);

          Console.ReadKey();
        }

    Qu’est ce qui se passe ? Pouvez-vous deviner l’output de l’exécution ? Le premier affichage sur la console car1.Speed a bien affiché 100. Le deuxième affichage car2.Speed a jeté une exception du type “NullReferenceException”.

    Dans le premier cas nous avons tout simplement assigné la valeur null à une copie du pointeur original. Cela n’a cependant aucune impacte sur vers quoi pointe car1. Dans le deuxième exemple nous ne passons pas une copie mais la valeur originale du pointeur (donc l’adresse mémoire du tas où est stocké l’objet car2).

    C’est très rare qu’on passe un type référence par référence. D’ailleurs on y réfléchissant je ne vois pas le cas où on aurait besoin de changer l’adresse du pointeur original vers autre chose.

    Conclusions

    Ce qu’on peut voir ce que les objets sont jamais passés par référence ni d’ailleurs par valeur. Une copie de son pointeur est faite ou le pointeur lui même est passé en paramètre. Il faut être très prudent lorsqu’on passe un type référence par référence car les effets de bord peuvent-être importants. Il est également important que chaque développeur comprenne les différences entre les types valeur et référence afin qu’ils évolue dans un environnement qu’ils connaissent.

    A bientôt pour une prochaine note :)

    Le Framework ASP.NET MVC est de plus en plus populaire. Dans la version Beta 2 de Visual Studio 2010 Microsoft nous fourni le template pour ASP.NET MVC 2 alors autant dire que son utilisation sera de plus en plus fréquente pour écrire des sites web. Le pattern MVC est très ancien qui date des années 70 il a juste été mis au goût dur jour avec ASP.NET MVC.

    Dans ce poste je ne compte pas de répéter la définition technique concernant ce pattern mais de rappeler quelques points fonctionnels à son sujet. Comme je l’ai noté dans un de mes posts précédents le pattern MVC peut être visualisé en se posant les question suivantes Model – qu’est-ce que c’est ?, View – de quoi ça a l’air ?, Controller – qu’est-ce que ça fait ?

    Quand on parle du pattern MVC, ce qu’il faut voir c’est la séparation de la présentation du modèle et du contrôleur de la vue. C’est un des principe les plus important dans l’architecture des application. En voici quelques raisons:

    1. Premièrement la présentation et la présentation n’ont pas les mêmes préoccupations. Quand on construire une présentation on pense plutôt à une interface agréable et ergonomique dotée d’une navigation claire pour l’utilisateur. Quand on travaille avec le modèle, ce qui nous intéresse c’est la logique métier et les interactions entre les objets métier.
    2. Quand on travaille avec la présentation et le modèle, souvent nous utilisons des librairies différentes.
    3. Nous pouvons développer plusieurs présentations pour le même modèle. Effectivement suivant le contexte dans lequel on se trouve nous pouvons choisir de présenter le même modèle des manières différentes avec des vue et des interfaces utilisateur différentes et tout ceci pour le même modèle.
    4. Un autre point important ce sont les tests. Il est plus facile de tester les objets non visuels. En séparant la présentation du modèle vous permet de tester toute la logique métier sans se préoccuper des problèmes lié à l’interface utilisateur (le rendu, sessions utilisateur, scripts, contextes, etc.)
    5. La présentation  à la connaissance du modèle alors que le modèle est complètement indépendant de la présentation. Le développement du modèle ne doit pas prendre en compte la présentation et comment elle doit être utilisée (en théorie). De même les changement apportés à la présentation n’altèrent pas le modèle.

    La séparation entre le contrôleur et le modèle a moins d’importance. Souvent les application mélangent la vue et le contrôleur ensemble ce qui a introduit les ambigüités lorsqu’on parle du pattern MVC. Le contrôleur est entre la vue et le modèle et sa responsabilité est de prendre en compte les actions de l’utilisateur, manipuler le modèle et de mettre à jour la vue.

    Les points que j’ai énumérés ne sont pas exhaustifs mais je pense qu’il sont assez représentatifs lorsqu’on parle du pattern MVC. Comme je disais au début, ça ne sert à rien de répéter la définition mais un petit rappel fonctionnel ne fera pas de mal :)

    A bientôt,

    Dernièrement après avoir passé une bonne matinée en cherchant un bug dans un des fichier de JavaScript de mon site web, je me suis posé la question s’il était possible de compiler les fichiers JavaScript et éventuellement inclure les résultats de la compilation dans les tests unitaires. En cherchant un peu sur Internet il n’est pas difficile de tomber sur la solution que j’ai cherchée. En fait elle est dans notre cher Framework.NET :) La réponse se trouve dans la librairie Microsoft.JScript. Il y a un compilateur JavaScript qui est capable de nous signaler toutes les erreurs qu’il a trouvé.

    Assez de parler, passons à la pratique. Regardons d’abord le fichier javascript que nous allons tester. Le fichier s’appelle “test-Bad.js” et contient le code suivant :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    function subtract(x,y) 
    {

    if (x = y) {
    return 0;
    }

    if (x < x) {
    return null;
    }


    switch (y) {
    case 0: z = x;
    case 1: z = --x;
    return z;
    }

    return; x-y;
    }

    Les erreurs suivantes sont à noter :

    ligne 3: La comparaison “==” devrait être faite au lieu de l’assignation.

    ligne 12: La variable “z” n’est pas déclarée. De même pour la ligne 13.

    Une erreur un peu particulière s’est produit à la première ligne qui nous indique qu’un type ou package est uniquement permis dans un fichier qui est compilé. Donc logiquement nous devrions avoir notre fichier qui commence par le mot clé “class” ou “package”.

    Passons au code C#. Tout d’abord vous devez avoir les références vers les librairies suivantes :

    1
    2
    using Microsoft.JScript; 
    using System.CodeDom.Compiler;

    Ensuite le code qui permet de compiler le JavaScript et de réporter les erreurs est très simple :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    JScriptCodeProvider jCodeProvider = new JScriptCodeProvider(); 
    CompilerParameters compilParams = new CompilerParameters();
    StringBuilder errorMessages = new StringBuilder();

    string file = @"test-Bad.js";

    using (StreamReader reader = File.OpenText(file))
    {
    string source = reader.ReadToEnd();
    CompilerResults results = jCodeProvider.CompileAssemblyFromSource(compilParams, source);

    foreach (CompilerError error in results.Errors)
    {
    errorMessages.AppendLine("Compilation JavaScript n'a pas réussie");
    errorMessages.AppendLine("\tCodeErreur: " + error.ErrorNumber);
    errorMessages.AppendLine("\t" + error.ErrorText);
    errorMessages.AppendLine("\t" + file + "(" + error.Line + ", " + error.Column + ")");
    errorMessages.AppendLine();

    Console.WriteLine(errorMessages);
    }
    }

    Console.ReadKey();

    Bien entendu il n’est pas optimisé mais son but est de vous montrer la simplicité avec laquelle nous pouvons compiler le JavaScript. Quelques explications s’imposent:

    JScriptCodeProvider fourni une instance vers le compilateur JScript.

    CompilerParameters représente les paramètres qui sont fournis lorsque vous appelez les compilateur. Pour le moment je n’ai pas encore découvert comment on peut les utiliser.

    Si vous lancez ce code vous devez avoir le résultat similaire:

     

    Comme vous pouvez le constater les erreur sont bien reportées (par contre je ne sais pas pourquoi parfois en double). Pour plus d’information sur les codes d’erreur je vous conseille de vous rendre ici : http://msdn.microsoft.com/en-us/library/by0atdkw(VS.71).aspx.

    Pour terminer le test je crée un autre fichier “test-Good.js” où je corrige les erreur signalées par le compilateur. Le code du fichier ressemble à ceci:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    class Math 
    {
    function Subtract(x, y)
    {
    var z = 10;
    if (x == y) {
    return 0;
    }


    if (x < x) {
    return null;
    }


    switch (y) {
    case 0: z = x;
    case 1: z = --x;
    return z;
    }

    return; x-y;
    }
    }

    Après avoir relancé le code (pensez à modifier le chemin d’accès vers le fichier à compiler) tout se passe très bien. Aucune erreur n’est rapportée.

    En conclusion je peux dire que cette bibliothèque peut s’avérer très intéressante dans l’écriture des tests unitaires qui testent vos fichiers javascript en compilation. Je n’ai pas encore testé les javascript très complexes donc je ne peux pas dire dans quelle mesure les résultats du compilateur sont fiables et si tous les scénario sont pris en charge. Je vous laisse de découvrir cette partie là si cela vous intéresse. Prenez ce post comme le point de départ.

    A bientôt.

    Ci-dessous quelques liens que je trouve très intéressant concernant les technos .NET.

    WPF & Silverlight

    ASP.NET

    • Il y a de nouveaux vidéos sur ASP.NET 4.0 et Visual Studio 2010 que vous pouvez visionner ici http://www.asp.net/learn/. Il y a aussi beaucoup de tutoriaux et de “How do”. Très bon endroit pour apprendre.

    Visual Studio 2010

    .NET

    • Microsoft a publié un document répertoriant les changement entre le .NET Framework 4.0 Beta 1 et Beta 2 pour WF et WCF. Vous pouvez le télécharger sur le site de Microsoft.
    • Un excellent article de Fabrice Marguerie sur les fuites de mémoire et de ressources en .NET (pdf).
    • Si vous connaissez et utilisez DomainDataSource (RIAServices) qui permet de placer la logique métier dans le modèle d’objet, je vous propose de vous intéresser de plus prêt de Versatile DataSources développé par Peter Blum. Vous y trouverez EntityDAODataSource et POCODataSource qui utilisent Dynamic Data et qui séparent la logique métier et UI.

    Entity Framework

    Dans le Framework 4.0 global CAS (Code Access Security) sera désactivé par défaut. Quel nouveau modèle sera utilisé ? Une courte explication s’impose.

    L’utilisation de CAS  n’était pas très toujours évidente, voir même très difficile. Beaucoup de développeurs comprennent mal comment correctement appliquer les attributs sur un bloc de code et quand est-ce qu’il est intéressant de le faire. Du point de vue de l’administrateur système cela est très difficile également où les polices de sécurité CAS doivent être configurées manuellement pour chaque permission mais comment savoir quelles permissions une application aurait-elle besoin ? Il est rare qu’un application expose sa liste de permissions dont elle a besoin. Pour les administrateurs système il est davantage conseillé d’utiliser les moyens plus efficaces pour gérer la sécurité comme par exemple Windows Software Restriction Policies. Ce qui est d’autant plus difficile, ce que pour chaque version .NET les polices CAS doivent être récrées.

    Un nouveau modèle s’appelle “Security Transparency”. Cela vous semble familier ? C’est normal, ce modèle a déjà été expérimenté avec Silverlight. Vous trouverez plus d’informations ici : http://msdn.microsoft.com/en-us/magazine/ee677170.aspx.

    Pour résumer rapidement, en voici les principales fonctionnalités:

    • Le code qui s’exécute normalement se voit attribuer les permissions “full trust”. Maintenant si vous utilisez Assembly.LoadFrom avec une URL votre assembly aura les permissions “full trust”.
    • Si le code mangé est hébergé il sera exécuté dans “sandbox” avec le permissions spécifiés par le hôte comme par exemple dans le cas de SQLCLR ou ClickOnce où les applications sont installées à partir d’internet. Dans le domaine d’applications sandboxé, chaque assembly s’exécute avec les permissions full ou partial trust.
    • Quand le domaine d’application est crée, pour les assmeblies en partial trust la liste explicite des permissions accordées doit être spécifiée. En même temps la liste des assemblies à qui on doit accorder full trust peut être jointe.
    • Il y a maintenant 3 niveaux de sécurité où votre code peut appartenir. Le plus haut “Critical” où le code en full trust peut tout faire. Au milieu nous trouvons la zone “Safe Critical” qui est un pont entre le niveau “Critical” et “Transparent” qui est au niveau le plus bas. Le code dans la zone “Transparent” est très restreint par le sandbox.

     

    En tout cas si vous voulez continuer d’utiliser CAS qui est désactivé par défaut, vous pouvez l’activer au niveau d’application dans le fichier app.config en activant le flag NetFx40_LegacySecurityPolicyenabled

    <configuration> 
       <runtime> 
          <!-- enables legacy CAS policy for this process --> 
          <NetFx40_LegacySecurityPolicyenabled="true" /> 
       </runtime>
    </configuration>

    En tout cas, je pense que le fait de mettre de côté CAS par Microsoft est une bonne chose. Son utilisation n’était pas facile.

    Vous avez certainement utilisé à de nombreuses fois le mot clé “is” pour tester la compatibilité d’un type d’objet au moment de l’exécution. Son utilisation est très simple:

    1
    if (instaceobjet is type)

    Cependant cela ne fonctionne pas très bien avec les types génériques si vous voulez effectuer une comparaison avec un type non assigné comme par exemple MyClass<T> où T n’est pas encore assigné.

    Vous pouvez faire par exemple :

    1
    if (customerValidator is IValidation<Customer>)

    mais cela ne fonctionnera pas :

    1
    if (customerValidator is IValidation<>)

    En cherchant un peu sur internet je suis tombé sur une proposition pour résoudre ce problème avec une méthode d’extension : http://stackoverflow.com/questions/457676/c-reflection-check-if-a-class-is-derived-from-a-generic-class (la réponse du 22 mai).

    C’est une méthode d’extension HasGenericDefinition() sur System.Type. Vous pouvez l’utiliser de la manière suivante:

    1
    if (customerValidator.GetType().HasGenericDefinition(typeof(IValidation<>)))

    Je sais que ceci n’est pas une méthode de plus élégantes mais pour le moment il n’existe pas dans C# d’autres possibilités (à ma connaissance) pour tester la compatibilité d’un type non assigné.

    Un autre manquement dans le genre:

    Casting à un type non assigné : IValidation<> validator = (IValidation<>)obj. Il y a peut être le moyen de s’en sortir avec la nouvelle fonctionnalité de C# 4.0 avec le type dynamic.

    Si vous avez d’autre exemple ou propositions de solution n’hésitez pas à me faire un retour.

    A bientôt,

    Il n’y a pas long temps Telerik a sorti un nouveau client en version beta pour la gestion des Work Items de TFS. L’outil est réalisé en WPF avec les contrôles fait maison par Telerik. Le client est téléchargeable à l’adresse http://www.telerik.com/products/tfsmanager-and-tfsdashboard.aspx. Je ne l’ai pas encore testé personnellement mais je voulais vous en faire part si cela vous intéresse de le tester de votre côté.

    Pour le moment le logiciel est GRATUIT à mon avis c’est à cause de sa version beta. Si vous le testez avant moi, n’hésitez pas à me faire un retour pour que je puisse mettre à jour mon post.

    Dans le gestionnaire des work items les contrôles de Telerik sont utilisés comme par exemple le Grid qui permet de grouper, trier, filtrer et changer de place de colonnes d’une manière native.

    C’est qui est très intéressant c’est le look du tableau de bord !

    Bon test !

    Plus de Messages Page suivante »


    Les 10 derniers blogs postés

    - TechDays Paris 2010 : La BI dans SharePoint 2010 par Blog Technique de Romelard Fabrice le il y a 1 heure et 29 minutes

    - TechDays Paris 2010 : Déploiement de nouvelles technologies – Retour d’expérience par l’informatique de Microsoft par Blog Technique de Romelard Fabrice le il y a 2 heures et 56 minutes

    - TechDays Paris 2010 : Plan de migration vers SharePoint 2010 par Blog Technique de Romelard Fabrice le il y a 6 heures et 39 minutes

    - TechDays Paris 2010 : La pleinière du second jour par Blog Technique de Romelard Fabrice le il y a 7 heures et 44 minutes

    - Visual Studio 2010 and .NET Framework 4 Release Candidate now available par Matthieu MEZIL le il y a 10 heures et 50 minutes

    - Création d’une base de donnée sous SQL Azure par Le Blog (Vert) d'Arnaud JUND le il y a 11 heures et 47 minutes

    - TechDays Paris 2010 : Les Services d’applications dans SharePoint 2010 par Blog Technique de Romelard Fabrice le il y a 21 heures et 46 minutes

    - TechDays Paris 2010 : La GED et SharePoint 2010 par Blog Technique de Romelard Fabrice le 02-08-2010, 16:54

    - TechDays Paris 2010 : SharePoint 2010 et Les réseaux sociaux par Blog Technique de Romelard Fabrice le 02-08-2010, 15:40

    - TechDays Paris 2010 : SharePoint 2010 – Description et nouveautés par Blog Technique de Romelard Fabrice le 02-08-2010, 14:33