:::: MENU ::::

Global Day of Code Retreat 2016 : une journée forte en émotions

Hier était un jour particulier pour les développeurs : le Global Day of Code Retreat 2016 (#gdcr2016 #coderetreat).Cet évènement, organisé dans 100 villes et rassemblant 2000 développeurs étaient l’occasion pour moi de faire un pas en avant dans des pratiques que j’ai vu personnellement, mais pas forcément professionnellement (le Test Driven Development), ou encore des nouveautés, comme le Ping Pong Pair Programming ou encore le Silent Pair Programming. Voici un résumé de ma journée !

Introduction et préparatifs

Je suis arrivé aux alentours de 8h30 (toujours matinal) devant le Webcenter d’AXA de Lille, où j’ai pu rencontré Xavier Balloy, l’un des organisateurs de l’évènement (avec Fabien Goguillon et Antoine Blancke). Après une petite discussion, on rejoint la « salle de coding » où les autres organisateurs finissaient les préparatifs : en attendant les autres on était bien servis : mini viennoiseries, cafés, jus de fruit… De quoi bien commencer la journée !

Une fois tout le monde arrivé, l’organisateur, Julien Jakubowski (j’ai oublié le terme exact, mes excuses…) nous a présenté le déroulement de la journée, (avec une vidéo d’Alexandru Bolboaca en prime nous expliquant l’importance de cette journée) : 3 sessions matin, repas 3 session après-midi, et un phase de « closing circle » (point où on dit ce que l’on a apprécié ou non, etc), le tout sur un même sujet (le Jeu de la Vie de Conway) et en effaçant son code à chaque session… Une session se compose en une phase de code en binôme (45mn) puis une phase de rétrospective (10mn) puis une pause de 5mn.

Pendant l’intégralité des sessions, on était relié par caméra avec les autres sessions Code Retreat et selon les fuseaux horaires on ne terminait donc pas tous en même temps : certains finissaient quand on commençait, on était au même créneau que l’Afrique du Sud visiblement et la ville de Bangalore a fini en début d’après-midi!

Maintenant, place aux sessions !

Session #1 : Introduction (Langage : C# .NET)

Pour la première session, en gros on faisait ce qu’on voulait, le but voulu était surtout de commencer à travailler en binôme, à partager des réflexions différentes, de voir notre comportement, etc. Pour cette première session, j’étais en binôme avec un ami qui faisait du C#.NET tout comme moi, mais n’ayant jamais fait de TDD et moi-même étant novice, on voulait le mettre en place, et on se posait beaucoup de questions compliquées (optimisation, grille, etc) alors qu’on en était pas à ce stade… Bref, je vois que j’avais une mauvaise approche du TDD !

Session #2 : TDD – Ping Pong Pair Programming (Langage : PHP)

Pour la deuxième session, on devait mettre en place du Ping Pong Pair Programming dans le cadre du TDD :

  • Le premier développeur doit écrire un test qui échoue
  • Le deuxième développeur doit faire en sorte de le corriger
  • Le premier développeur doit refactoriser le code
  • Le deuxième développeur doit écrire un test qui échoue
  • Etc

Ici, je suis sorti de ma zone de confort en faisant du PHP : je n’en ai pas fait depuis 5 ans, et la reprise a été très douloureuse, je ne connaissais pas l’environnement de test, mais mon binôme était très pédagogue et on a bien avancé en 45mn!

Session #3 : Contrainte : Pas de for ou pas plus de 4 instructions par méthode (Langage : Java)

Pour cette session, je me suis remis à mon langage scolaire, le Java ! (tout mon entourage professionnel sait que je n’aime pas ce langage…) Et avec mon binôme, comme contrainte nous avons choisi de ne pas utiliser de boucle for (la subtilité de la contrainte était de ne pas utiliser d’index, le foreach en Java8 était autorisé apparemment). Mais nous n’avons même pas pu arriver jusqu’au stade des boucles : un problème de conflits entre environnement de test (JUnit3 et JUnit4) a fait qu’on a perdu 15mn… Finalement on a utilisé JUnit4, et c’est passé, mais avec 15mn de retard on n’a pas eu le temps d’aborder tout le sujet… Mais on a fait avec, et l’important c’est l’apprentissage! Maintenant, c’est l’heure de manger 🙂

Session #4 : 2mn avant suppression ! (Langage : Java)

Toujours en Java avec un autre binôme, ici on est passé en JUnit3, ce qui m’a surpris. Le handicap sur cette session était de taille : on avait 2mn pour faire un test qui échoue, et le corriger, tout en se parlant. Si les 2mn sont écoulés, on fait un git-reset, tout simplement. Et c’est ici que j’ai pu remarquer que connaitre son IDE sur le bout des doigts est primordial… Autant sous Visual Studio en C# j’aurais pu mieux m’en sortir, autant ici j’ai dû faire beaucoup de git reset. Je voyais mon binôme avoir l’approche TDD (créer sa ligne d’abord dans sa classe de test, puis faire générer les méthodes de la classe) tandis que moi je faisais la méthode inverse, beaucoup plus longue. On voulait aussi en faire trop, ce qui m’a permis de voir que les tests devaient reposer sur des choses simples, étapes par étapes, et pas sur des gros bouts de codes.

https://twitter.com/xavierballoy/status/789794785334812672

Session #5 : Silent Pair Programming (Langage : C++)

Sans doute l’épreuve LA plus violente pour moi : un langage que je n’ai presque vu qu’à titre personnel il y a une dizaine d’années (je l’ai vu rapidement à la fac), et je n’ai aucun moyen de parler à mon binôme : ni par voix, ni par langage des signes, ni par commentaires, … Et quand j’arrive sur le poste, aucun TDD ! Mon binôme utilisait des Vector, je n’avais jamais utilisé ça, et je devais trouver comment obtenir la taille de cet élément, si c’était un size() comme PHP, ou un Length/Count comme C#! Autant dire que j’étais dans cette optique là :

ted-mosby

A la fin de l’épreuve, mon binôme m’a dit que c’était parce qu’il a eu un problème lors de l’installation de l’environnement. Pendant cette épreuve, le temps m’a paru long, et j’ai pu voir l’importance du nommage des variables, classes et méthodes (on a failli réécrire 2 fois la même chose). Cela nous force également à accepter la vision de l’autre, de chercher à la comprendre, alors que par discussion cela aurait pu prendre facilement 15mn.

Vous la ressentez ma détresse non? :p

Session #6 : Contrainte choisie : Aucun type primitif dans les méthodes (Langage : NodeJS)

Pour cette dernière épreuve (ouf, on peut parler !) on a eu le choix entre 3 contraintes :

  • Pas de souris ni trackpad, ce qui convenait à peu de personnes
  • Nom de classes et méthodes imposées par des dés, ce qui convenait à un peu plus de personnes (dont moi)
  • Interdiction d’utiliser des types primitifs dans les signatures de méthodes publiques, ce qui convenait à la majorité (dont moi)

Cela peut paraître bizarre à première vue, mais le nommage est tellement utile pour ne pas perdre le temps, et la souris utile quand on ne connait pas l’IDE, qu’au final ça ne me surprenait pas. Je suis revenu avec mon binôme de départ et ami pour faire du NodeJS (ECMAScript 6 donc), il a voulu en même temps m’expliquer tout le système Node, et on a commencé à structurer en classe, la norme ES6 ayant introduit ce mot clé. On a eu beaucoup de souci, étant encore des débutants dans NodeJS, mais cela nous a permis de susciter notre curiosité.

Rétrospective et conclusions

On s’est ensuite retrouvé dans une salle (la même qu’au repas) boire un verre et discuter, tout en pouvant noter la journée via des post-its : en mettre sur une colonne « + », sur une colonne « – » et sur une colonne suggestions. En suggestion j’ai mis qu’il faudrait pouvoir dire à l’avance les langages maitrisés pour choisir plus facilement le binôme, car on avait parfois à peine 2mn pour le choisir. Et en négatif j’ai mis en avant l’aspect frustrant du travail inachevé (on doit supprimer le code à chaque session), ce qui pousse à faire le Jeu de la Vie en rentrant chez soi ! Et en positif je n’ai rien mis car les autres avaient déjà évoqué ce que je pensais.

Pour résumer cette journée, elle était épuisante riche en émotions, cela m’a apporté énormément de bien de trouver des développeurs passionnés et de pouvoir coder avec eux sans deadlines, sans stress, ni rien, en dehors du monde de l’entreprise. Je n’ai pas sorti mon PC du sac car je voulais me forcer à sortir de ma zone de confort, et avec 5 sessions hors .NET, je pense avoir réussi mon pari ! Cela m’a permis de voir l’intérêt du TDD, du pair programming et aussi que sur un même problème on a tous une approche à la fois différente et pertinente.

C#, PHP, Java, C++, Javascript… j’ai fait la moitié de la liste mine de rien! 🙂

J’espère que ça améliorera mon code quotidien (le soir même, on parlait encore de code avec Angular alors que notre cerveau était épuisé…) et que j’aurais l’occasion de participer à la journée Code Retreat l’année prochaine !

J’aimerais remercier les organisateurs pour cet évènements (Julien Jakubowski et l’équipe AXA) et plus particulièrement ma compagne, qui me soutient dans toutes ces activités car on dit souvent qu’en tant que développeur on ne doit pas compter ses heures, mais en tant que développeur passionné la veille technologique nous incite à ne pas les compter et on a tous de la chance d’avoir eu un entourage compréhensif en ce samedi! 🙂


Créer ses Data Annotation Validator personnalisées (ASP.NET MVC)

Edit : J’avais toujours dit de faire un petit rafraichissement de ce tutoriel avec notamment la partie unobtrusive Javascript, c’est chose faite!

En ASP.NET MVC, quand vous mettez en place une validation de formulaire, il est très utile d’utiliser les Data Annotation Validator sur le modèle pour vous épargner toute la logique de test de code. Le Framework .NET en comprend nativement plusieurs :

  • Range : permet la validation si la valeur de la propriété testée est dans une certaine tranche
  • RegularExpression : permet la validation si la valeur de la propriété testée respecte la RegEx fournie
  • Required : Comme son nom l’indique, permet d’indiquer une propriété comme requise
  • StringLength : Dans le cadre d’une propriété de type String, spécifie la longueur maximum de chaîne.
  • DataType : permet la validation si la valeur de la propriété testée repecte le type spécifiée (DataType.Date par exemple)
  • Validation : classe de base pour tous les attributs de validation.

Pour plus de détails sur le fonctionnement des Data Annotation Validators, vous pouvez consulter ce lien : ASP.NET

Ce qui nous intéresse ici n’est pas de revoir toutes ces bases, mais d’envisager une chose : si on doit faire une validation qui n’est pas prévue par le Framework .NET, comme par exemple une validation qui dépend d’une autre propriété? On devra passer par nos propres Data Annotation Validators, ce qui peut devenir très intéressant.

Je vais dans ce ticket prendre le scénario suivant : j’ai une liste de radiobutton, avec des textboxes associés : dès qu’une radiobutton est sélectionnée, sa textbox devient obligatoire. Et bien on peut implémenter ça vie un Custom Validator, et c’est ce qu’on va faire tout de suite!

Mise en place de notre solution : MvcCustomAttributeExample

Tout d’abord, créons une nouvelle solution « MvcCustomAttributeExample » pour voir cette implémentation en action. Je vais tout d’abord créer le modèle « TestModels » qui va représenter mon exemple, avec mes différents attributs.

public class TestModels
{
///
<summary>
/// Type
/// </summary>

[Display(Name = "Type")]
public string Type { get; set; }
///
<summary>
/// Contenu A
/// </summary>

[Display(Name = "Contenu A")]
[StringLength(500, ErrorMessage = "50 caractères maximum.")]
public string ContenuA { get; set; }

///
<summary>
/// Contenu B
/// </summary>

[Display(Name = "Contenu B")]
[StringLength(500, ErrorMessage = "500 caractères maximum.")]
public string ContenuB { get; set; }
}
Puis un contrôleur… (à noter que je redirige directement vers l’édition pour éviter de mocker une liste)
//
// GET: /Test/
public ActionResult Index()
{
return RedirectToAction("Edit", new { id = 1 });
}
public ActionResult Edit(Int32 id)
{
TestModels test = new TestModels();
return this.View(test);
}
[HttpPost]
public ActionResult Edit(TestModels test)
{
if (ModelState.IsValid)
{
return this.RedirectToAction("Index");
}return this.View(test);
}
… et une vue associée, générée presque automatiquement (la propriété Type a été gérée en tant que RadioButton, et je l’ai géré manuellement pour faire rapidement ce tutoriel)  :
@model MvcCustomAttributeExample.Models.TestModels

@{
    ViewBag.Title = "Edit";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Edit</h2>

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()
    
    <div class="form-horizontal">
        <h4>TestModels</h4>
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })

        <div class="form-group">
            @Html.RadioButtonFor(model => model.Type, "A")
            @Html.LabelFor(model => model.ContenuA, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.ContenuA, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.ContenuA, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.RadioButtonFor(model => model.Type, "B")
            @Html.LabelFor(model => model.ContenuB, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.ContenuB, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.ContenuB, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Save" class="btn btn-default" />
            </div>
        </div>
    </div>
}
Accéder à votre url : http://localhost:XXXXX/Test , vous devriez avoir ceci :CustomAttribute_01

Mise en place du Custom Validator

Maintenant, si on veut ajouter une condition, qui dit que selon la valeur du type, tel ou tel textbox est obligatoire, comment feriez-vous ? Cela peut vite être compliqué… C’est là que le Custom Validator vient à notre secours !

Voilà la classe que j’ai implémentée dans le dossier CustomAttributes créée pour l’occasion :

/// &lt;summary&gt;
/// Spécifie qu'une valeur de champ de données est requise et est liée à une case à cocher
/// &lt;/summary&gt;
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public class RequiredWithRadioButtonAttribute : ValidationAttribute
{
    /// &lt;summary&gt;
    /// Nom de la propriété de la case à cocher
    /// &lt;/summary&gt;
    private String _radioButtonProperty;

    /// &lt;summary&gt;
    /// Valeur de la case à cocher à tester
    /// &lt;/summary&gt;
    private String _valueToCompare;

    /// &lt;summary&gt;
    /// Initialise une nouvelle instance de la classe RequiredWithRadioButton
    /// &lt;/summary&gt;
    /// &lt;param name="radioButtonProperty"&gt;&lt;/param&gt;
    /// &lt;param name="radioButtonValue"&gt;&lt;/param&gt;
    public RequiredWithRadioButtonAttribute(String radioButtonProperty, String radioButtonValue)
   {
        _radioButtonProperty = radioButtonProperty;
        _valueToCompare = radioButtonValue;
    }

    /// &lt;summary&gt;
    /// Vérifie que la valeur du champ de données requis n'est pas vide.
    /// &lt;/summary&gt;
    /// &lt;param name="value"&gt;Valeur de champ de données à valider.&lt;/param&gt;
    /// &lt;param name="validationContext"&gt;&lt;/param&gt;
    /// &lt;returns&gt;true si la validation réussit ; sinon, false.&lt;/returns&gt;
    protected override ValidationResult IsValid(Object value, ValidationContext validationContext)
    {
        PropertyInfo propertyInfo = validationContext.ObjectType.GetProperty(this._radioButtonProperty);
        Object otherPropertyValue = propertyInfo.GetGetMethod().Invoke(validationContext.ObjectInstance, null);
        if (!(otherPropertyValue is String))
            throw new NotSupportedException("La propriété de comparaison doit être de type string");
        String sValue = (String)otherPropertyValue;
        Boolean isEmptyValue = (value is String) ? String.IsNullOrEmpty((String)value) : (value == null); // Si on a une TextBox, elle renverra une chaine vide plutôt que null
        if (isEmptyValue &amp;&amp; String.Equals(sValue, _valueToCompare))
            return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
        return ValidationResult.Success;
    }
}

On nomme notre attribut selon une certaine convention, il doit avoir comme suffixe Attribute. Notre attribut a deux propriétés privées, qui contiennent le nom du radioButton et la valeur attendu du radioButton valueToCompare. Ensuite, dans la méthode « IsValid », on va tester le contenu du radioButton pour le comparer avec valueToCompare. C’est aussi simple que ça ! (J’avoue que la reflection peut faire un peu peur… 🙂 )

Vient ensuite l’implémentation dans notre modèle :

public class TestModels
{
    /// &lt;summary&gt;
    /// Type
    /// &lt;/summary&gt;
    [Display(Name = "Type")]
    public string Type { get; set; }
    /// &lt;summary&gt;
    /// Contenu A
    /// &lt;/summary&gt;
    [Display(Name = "Contenu A")]
    [RequiredWithRadioButton("Type", "A")]
    [StringLength(500, ErrorMessage = "50 caractères maximum.")]
    public string ContenuA { get; set; }

    /// &lt;summary&gt;
    /// Contenu B
    /// &lt;/summary&gt;
    [Display(Name = "Contenu B")]
    [RequiredWithRadioButton("Type", "B")]
    [StringLength(500, ErrorMessage = "500 caractères maximum.")]
    public string ContenuB { get; set; }
}

Comme vous pouvez le voir, ici il n’y a plus le suffixe « Attribute ». On renseigne nos deux paramètres du constructeurs, et le ErrorMessage est quant à lui issu de l’héritage de ValidationAttribute.

Et… c’est tout ! Cela suffit à implémenter notre règle de gestion, comme on peut voir sur cet aperçu écran :

 CustomAttribute_02

Et la partie Cliente dans tout ça ?

Cependant, il reste encore une chose à voir, et qui est essentielle. Jusqu’à présent, on est obligé de faire un aller-retour serveur pour faire le contrôle. Mais cela peut être évité par l’utilisation de l’unobtrusive Javascript, ce qu’on va faire à l’instant !

Nous allons déjà implémenter les librairies nécessaires dans notre Layout, cela tombe bien car on a un bundle tout prêt se nommant « jqueryval » qui est présent dans le BundleConfig :

@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/jqueryval")
@Scripts.Render("~/bundles/bootstrap")
@RenderSection("scripts", required: false)

Maintenant qu’on a rempli les prérequis, il est temps de se mettre au travail ! On va reprendre notre classe RequiredWithRadioButtonAttribute, en lui spécifiant qu’on veut la rendre « validable » via jquery : pour ceci il faut implémenter l’interface IClientValidatable et sa méthode associée GetClientValidationRules.

/// &lt;summary&gt;
/// Spécifie qu'une valeur de champ de données est requise et est liée à une case à cocher
/// &lt;/summary&gt;
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public class RequiredWithRadioButtonAttribute : ValidationAttribute, IClientValidatable
{
    /// &lt;summary&gt;
    /// Nom de la propriété de la case à cocher
    /// &lt;/summary&gt;
    private String _radioButtonProperty;

    /// &lt;summary&gt;
    /// Valeur de la case à cocher à tester
    /// &lt;/summary&gt;
    private String _valueToCompare;

    /// &lt;summary&gt;
    /// Initialise une nouvelle instance de la classe RequiredWithRadioButton
    /// &lt;/summary&gt;
    /// &lt;param name="radioButtonProperty"&gt;&lt;/param&gt;
    /// &lt;param name="radioButtonValue"&gt;&lt;/param&gt;
    public RequiredWithRadioButtonAttribute(String radioButtonProperty, String radioButtonValue)
    {
        _radioButtonProperty = radioButtonProperty;
        _valueToCompare = radioButtonValue;
    }

    /// &lt;summary&gt;
    /// Vérifie que la valeur du champ de données requis n'est pas vide.
    /// &lt;/summary&gt;
    /// &lt;param name="value"&gt;Valeur de champ de données à valider.&lt;/param&gt;
    /// &lt;param name="validationContext"&gt;&lt;/param&gt;
    /// &lt;returns&gt;true si la validation réussit ; sinon, false.&lt;/returns&gt;
    protected override ValidationResult IsValid(Object value, ValidationContext validationContext)
    {
        PropertyInfo propertyInfo = validationContext.ObjectType.GetProperty(this._radioButtonProperty);
        Object otherPropertyValue = propertyInfo.GetGetMethod().Invoke(validationContext.ObjectInstance, null);
        if (!(otherPropertyValue is String))
            throw new NotSupportedException("La propriété de comparaison doit être de type string");
        String sValue = (String)otherPropertyValue;
        Boolean isEmptyValue = (value is String) ? String.IsNullOrEmpty((String)value) : (value == null); // Si on a une TextBox, elle renverra une chaine vide plutôt que null
        if (isEmptyValue &amp;&amp; String.Equals(sValue, _valueToCompare))
            return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
        return ValidationResult.Success;
    }

    public IEnumerable&lt;ModelClientValidationRule&gt; GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        var clientValidationRule = new ModelClientValidationRule()
        {
            ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
            ValidationType = "requiredwithradiobutton"
        };

        clientValidationRule.ValidationParameters.Add("radiobuttonproperty", this._radioButtonProperty);
        clientValidationRule.ValidationParameters.Add("valuetocompare", this._valueToCompare);
        return new[] { clientValidationRule };
    }
}

Plusieurs choses à dire concernant la méthode : tous les paramètres sont en minuscules, et c’est volontaire : cela va partir côté Javascript, et cela va déclencher une erreur si on a une syntaxe du style CamelCase. Ensuite, le nom donné au ValidationTYpe ainsi qu’aux ValidationParameters est très important, il nous servira après !

Maintenant, fini le C#, fini le serveur, place au Javascript et pour l’occasion on va créer un fichier pour l’occasion, « customValidations.js ».

Tout d’abord, on va créer l’adapter unobtrusive. Voilà comment il se présente :

jQuery.validator.unobtrusive.adapters.add("requiredwithradiobutton", ["radiobuttonproperty", "valuetocompare"], function (options) {
    options.rules['requiredwithradiobutton'] = {
        radiobuttonproperty: options.params.radiobuttonproperty,
        valuetocompare: options.params.valuetocompare
    };
    options.messages['requiredwithradiobutton'] = options.message;
});

Ici, on retrouve la cohérence entre notre CustomAttribute C# et notre adapter unobtrusive au niveau des paramètres. On implémente notamment options.rules, pour attribuer nos différents paramètres.

Maintenant, on passe à la méthode en elle-même :

jQuery.validator.addMethod("requiredwithradiobutton", function (value, element, params) {
    var radioButtonValue = $('#' + params.radiobuttonproperty).val();
    if (radioButtonValue != params.valuetocompare)
        return true;
    else if (value != '')
        return true;
    else
        return false;
});

Ici, j’ai dans value la valeur de la TextBox, et dans params les paramètres récupérés depuis l’attribut C#, on a tout ce qu’il faut pour faire notre test !

Maintenant, il reste à implémenter notre fichier dans le Layout :

@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/jqueryval")
@Scripts.Render("~/bundles/bootstrap")
@RenderSection("scripts", required: false)

Et nous avons fini ! Maintenant, à chaque saisie de texte et perte de focus d’une textbox, un contrôle client sera effectué. Autant dire que pour des contrôles complexes, le Javascript simplifie la vie (comme toujours vous me direz…). Vous constaterez qu’à aucun moment, que ce soit partie serveur ou partie client, j’ai touché au contrôleur ou à la vue : j’ai uniquement touché au modèle par le biais d’un ajout d’attribut, ce qui permet à cette classe d’être réutilisable à volonté!

Conclusion

En résumé dans ce ticket vous avez appris à créer rapidement un Validator complet, mais les possibilités sont immenses : vous pouvez appliquer ce même principe avec une checkbox, s’assurer que des mots de passes sont identiques avant confirmation d’enregistrement, gérer si une date est inférieure à une autre date…
Pour aller plus loin, je vous conseille de lire ce ticket de blog très intéressant : Blog de Brad Wilson ainsi que cet autre ticket de DevTrends qui aborde le sujet de manière complète.
L’ensemble de la solution présente dans ce tutorial est présente dans un dépôt GitHub disponible à cette adresse.
Bon développement!

Une journée aux Experiences

Tout d’abord désolé d’avoir délaissé aussi longtemps ce blog : entre l’auto-formation et les entretiens je n’avais plus vraiment le temps, mais cela devrait aller mieux maintenant! 🙂

Mercredi a été une journée très importante pour moi : ma première participation aux Microsoft Experiences (anciennement Tech Days) du haut de mes 5 années d’expériences. Cela peut paraître anodin pour certains, mais pour les gens de la province, cela n’est pas forcément évident de se déplacer pour assister à ce genre de conférence (transport, employeurs…). Pour ma part, ayant posé ma démission pour diverses raisons, la question ne se posait pas : RTT + TGV, et c’était réglé.

On commence la journée par un réveil à 5h00, une sortie du petit chien pour se mettre en forme, une toilette, petit déjeuner en écoutant sa radio préférée (#sponsor VirginRadio) et hop, on va prendre le train de 6h45 en se trompant de gare et en croisant un ancien collègue qui s’était lui aussi trompé de gare (les joies du matin et surtout de deux gares proches ayant chacun des départs pour Paris). Le trajet et les circonstances font que l’on arrive vers 8h45 devant le Palais des Congrès, top donc.

Récupération du collier pour le badge fait, on se dirige vers la plénière. Pendant des heures c’était le show (avec un arrière goût de business cependant) : on a pu assister à la démonstration de quelques acteurs majeurs du secteur IT comme Orange, Dassault Systèmes, AccorHotels ou encore Docker.

Plénière (9:30-11:00) : Moitié business, moitié technique, pendant une journée technique…

Microsoft Experiences

Description et speakers

Etant l’une des rares (la seule ?) sessions auquel j’ai assisté qui soit filmé, je ne vais pas en dire grand-chose, hormis le fait que des grandes entreprises se sont exprimés et ont montré comment ils utilisaient les technologies Microsoft (AccorHotels avec le nouveau StaffHub d’Office365 ou encore Orange Business Services avec Xamarin), avec des démos parfois « gadget » (comme le déblocage du PC par un second téléphone via application : pourquoi ne pas utiliser Authenticator ?) : la frontière avec la journée Business était mince… Heureusement, le duo Scott était là pour nous en mettre plein la vue avec Azure (fonctionnalités, annonces de l’ouverture de 2 datacenters en France) et on a eu le droit à une petite démo du Bot Framework par Sebastien Petrus, ce qui remontait un peu le niveau technique de l’ensemble.

Mes respects au directeur de Docker : suite à la mauvaise gestion du temps, le pauvre a vu tout le monde partir au fur et à mesure de son discours, car personne ne voulait rater sa session de 11h30 (d’ailleurs, on voit énormément de chaises vides à la fin de la vidéo). Pour ma part je ne suis pas resté jusque la fin et j’ai bien fait : contrairement à ce qui a été annoncé par Christophe Gaume dans la vidéo, comme la salle était pleine, la session a bien commencé à 11h30 (deux amis ont d’ailleurs loupé leur première session…).

Lien de la vidéo

Les sessions

Lors de la plénière, un collègue m’a dit « prévois des plans B, au cas où tu ne peux pas rentrer dans les salles ». Conseil sage, étant donné que comme je l’ai dit précédemment des amis ont loupé leur première session ! Fort heureusement, je n’en ai raté aucune.

ASP.NET Core 1.0 et Microsoft Azure : les dessous de la plateforme Microsoft Expériences (11:30-12:15)

Description et speakers

 On parle beaucoup d’ASP.NET Core 1.0 ces temps-ci, mais au final je ne l’ai pas pratiqué, donc je suis venu intrigué à cette session. Ici on avait un site fonctionnel (le site Experiences, qui gérait à la fois la réservation, avec les badges, l’agenda, etc), et les intervenants d’Infinite Square (Florent Santin et Sébastien Ollivier) ainsi que l’intervenante Microsoft France (Mélanie Daboudet) ont évoqué les besoins : auparavant ils étaient assez disparates, pour les Microsoft Experiences ils ont voulu unifier tout ça, ce qui a conduit au produit « inwink », plateforme SaaS de gestion d’évènements B2B (développement cloud multi tenants, extensible par API).

Ce que j’ai beaucoup apprécié dans cette conférence c’est qu’ils ont décortiqué toute l’architecture, pourquoi ils ont fait comme ça, pourquoi le choix d’ASP.NET Core 1.0 (qui était au moment du développement en version béta), etc.

Pour eux, au niveau de l’ASP.NET Core les avantages sont entre autres :

  • La portabilité (au revoir IIS)
  • La performance
  • La légèreté
  • La modularité (on ne charge pas tout le cœur .NET ou tout le cœur de MVC)

Pendant leurs développements, en dehors du problème de la bêta et de l’outillage, ils ont constaté qu’on a une montée en compétence rapide et que l’ASP.NET Core 1.0 est entre autre léger, rapide et 100% asynchrone.

Ils ont également parlé de leurs bases de données et de l’aspect Azure, mais ne voulant pas retranscrire intégralement la conférence par souci de plagiat je vous invite à contacter les animateurs, je pense qu’ils seront ravis de partager plus en détail leur expérience !

 

Ensuite on va chercher un sandwich, on mange en 30mn chrono, et on part pour la deuxième session !

Microsoft Composition, Pierre Angulaire de vos applications ? (13:15-14:00)

Description et speakers

Mon deuxième coup de cœur, tout simplement. Pourquoi ?

On met dans le même panier:

  • Du développement UWP
  • Des animations très bien faites, plus optimisé que les Storyboard
  • Une démo parfaitement adapté à l’actualité (Stranger Things, série à succès)
  • Un développeur (Samuel Blanchard) + un designer (Benjamin Launay), le duo gagnant

Alors pourquoi ce n’est pas mon coup de cœur ? On y reviendra après…

J’ai pu voir le comportement de Microsoft Composition, qui permet de faire énormément d’animations assez facilement, allant même jusqu’aux effets Frosted Glass (effet mise au point, comme on peut voir au cinéma quand on veut donner de l’attention à l’avant-plan ou l’arrière-plan en floutant le reste). De plus, Benjamin a apporté sa petite touche avec plein de références en matière de design (ce qui est bon de faire dans une application, les codes à respecter, la police utilisé dans Stranger Things qui est utilisé aussi dans les films de Stephen King et plus généralement dans les films des années 80 etc.), et quelques anecdotes sur les années 80/90. Il y avait une vraie complémentarité dans le discours, c’était vraiment agréable. Bref une conférence geek très décontractée !

Javascript : le futur c’est maintenant ! (14:30-15:15)

Description et speakers

Honnêtement, je me suis demandé ce que je faisais là, en milieu de conférence. Pas parce que le speaker était mauvais, d’ailleurs Christophe Porteneuve est il me semble réputé pour être un monstre en Javascript (il faisait plein de trolls pour animer un sujet pas forcément facile, c’était sympa), mais plutôt parce que j’en attendais plus : en lisant « le futur c’est maintenant » : je m’attendais à du TypeScript, du NodeJS, de l’AngularJS, du ReactJS, pourquoi choisir l’un et pas l’autre, ce genre de chose. Au final, ces concepts ont été très peu abordés, beaucoup trop de temps a été passé sur Babel (transpileur capable de compiler des sources ECMAScript 2015 en dehors des proxies), on a parlé également d’Electron, de Flow, mais on n’a pas vu énormément de code à mon goût. Classé niveau 100, je n’étais visiblement pas la cible de cette conférence… Je ne reproche rien au speaker encore une fois, qui lors des questions a bien montré qu’il savait de quoi il parlait. Cela m’apprendra la prochaine fois à bien vérifier les niveaux des conférences avant d’y aller !

Construisons des applications vraiment universelles avec Windows, Xamarin et MVVM (15:45-16:30)

Description et speakers

Imaginez-vous en train de faire souvent des verrines en pâtisserie. Maintenant imaginez-vous en train de rencontrer en personne le créateur de la verrine (Philippe Conticcini, un dieu vivant) qui vous explique son art. Ici c’est la même chose, avec Laurent Bugnion et son petit bébé MVVM Light, plugin que j’utilise depuis 3 ans, qui nous a expliqué le pattern MVVM tout en expliquant un tas d’autres trucs. J’étais comme un gosse, au premier rang, comme peut l’attester la photo qu’il a faite !

Dans sa conférence il a détaillé comment faire une « vraie » application universelle via Xamarin : cibler à la fois les applications universelles (UWP) et Android/iOS. En créant plusieurs projets, il a détaillé comment bien implémenter MVVMLight, l’utiliser via les snippets, où déporter tel code, comment faire ses tests unitaires, tout cela illustré via une application « Flowers » déposé sur GitHub : c’était très instructif, c’est simple, j’avais le sourire du début à la fin !

Pour l’instant, cela reste la plus belle rencontre de ma carrière de développeur .NET, tout simplement.

Les nouveautés de C# 7.0 (17:00-17:45)

Description et speakers

Le C# 7.0 apparait avec Visual Studio 15 (à ne pas confondre avec 2015) Preview 3. Que de chemins parcourus depuis le début ! La première chose qui m’a choqué : la queue ! Avant l’ouverture, on était plus presque une centaine à faire la queue 15mn avant l’ouverture des portes.

Mitsuru Furuta a su mettre en avant assez simplement les nouveautés du langage (en le comparant notamment à F#), c’était purement technique, il fallait s’accrocher, car ça allait parfois vite et on ne voyait pas forcément en bas de l’écran s’il y avait une grande personne devant nous, mais ça valait le coup !

Les nouveautés concernent entre autre les Literrals, les Out Variables, le Ref Returns, les Local Functions, le Pattern Matching ou encore les Tuples. Je ne vais pas détailler toutes les nouveautés ici, car elles sont disponibles sur le site MSDN.

What’s new in C# 7.0

Windows 10 : Les nouveautés de la plateforme universelle (UWP) (18:15-19:00)

Description et speakers

Pour la dernière conférence, j’ai pu rencontrer Jonathan Antoine, une personne que j’ai rencontrée sur un groupe Facebook de développeurs Windows Apps, qui a animé la conférence avec son collègue Jérôme Giacommi. Ici c’était le tour d’horizon du SDK UWP Windows 10 : On retrouve du Composition (que l’on a pu voir sur une session précédente), mais aussi la gestion des GIF, le fait de pouvoir cibler un Framework minimum en plus du Framework cible… On a aussi vu de l’App Service en action : Jonathan a enclenché une action sur son téléphone, pour qu’ensuite cela déclenche une animation sur sa Surface. Enfin, on a surtout parlé extensions : chaque application pourra avoir son lot d’extensions, ce qui est très intéressant ! Petit bémol, cela ne s’applique pas (encore) à tout ce qui est UI. Pour conclure la session, ils ont parlé de Connected Animation (le fait de préparer l’animation dans la page source et de la déclencher dans la page de destination) puis du Store Services SDK et de l’UWP Community Toolkit.

J’ai pu discuter avec Jonathan à la fin, et ce fut très sympathique d’avoir une discussion spectateur/speakers (auparavant j’étais toujours pressé de rater la prochaine conférence…) pour discuter de notre passion commune, et tout au long de la session j’ai pu voir qu’ils maitrisent bien leur sujet. Je suis reparti avec un livre rédigé par Jonathan, ça me fera un petit souvenir et une compétence de plus à acquérir!

Maintenant, on rentre!

Après avoir eu des étoiles plein les yeux, il est temps de rentrer : on mange dans un fast-food, on attend son train en retard (ceux qui était à Paris Nord comprendront :p ) et on se détend dans le train, en repensant à ce marathon et en étant pressé de rejoindre sa petite famille (vers 22h30, bonne journée marathon!) qu’on a laissé toute la journée.

wp_20161005_20_48_49_pro

Petits souvenirs

Bilan

Pour résumer cette journée, avec la plénière et 6 conférences je dirais que j’ai axé mon programme principalement sur ce que j’aime personnellement, à savoir le monde UWP (tout en ne négligeant pas mon aspect professionnel : Web, donc ASP.NET) et cela m’a fait plaisir de voir qu’il y a un public, malgré les annonces récentes et la communication parfois désastreuse de Microsoft dans le domaine mobile, que j’affectionne tout particulièrement. Pour l’évènement en lui même, les créneaux étant serrés, je n’ai pas du tout eu le temps de voir les stands ni rien (j’ai eu l’occasion d’échanger avec un commercial dans un escalator, on s’est raconté brièvement notre journée, c’était très sympathique!), je me suis contenté uniquement des sessions… mais c’est le principal! Cela fait plaisir de voir autant de speakers, autant de sujets abordés, et surtout, autant de passionnés! J’espère venir l’année prochaine 🙂

PS : J’essaierai de mettre les liens GitHub prochainement, en tout cas ceux que j’ai en ma possession!

 

 


Petit glossaire (non exhaustif) sur la syntaxe Razor

Bonjour!

Pas évident de se repérer avec la syntaxe du moteur Razor quand on vient de mettre un pied dans le monde d’ASP.NET MVC n’est-ce pas? On peut être amener sur une même page à la fois du HTML, du CSS, du Javascript et du C#… autant dire qu’il faut maîtriser!

Le symbole clé pour ce moteur, c’est celui ci : @ .

Insertion d’une propriété (style modèle) :

<span>@model.MaPropriete</span>

Ici, on voit que la logique Razor ne s’applique qu’à l’intérieur de la balise span : cette syntaxe est utile pour alimenter une vue à partir d’un modèle. Cependant, on peut parfois avoir besoin d’afficher du code HTML, ce qui n’est pas possible par défaut pour raison de sécurité. On doit passer par une autre fonction :

<span>@Html.Raw(model.MaPropriete)</span>

Insertion d’un bloc de code

@foreach(var p in proprietes) {
<span>Texte</span>
}

Attention à ceci : lorsqu’on insère un bloc comme le foreach, Razor s’attend à ce que l’intérieur du bloc soit du contenu HTML. Si vous voulez utilisez à nouveau du Razor, il suffit de le réinterpréter comme vu précédemment!

Mais parfois, vous aurez besoin d’écrire plusieurs lignes de code, plutôt qu’utiliser des @partout. Pour insérer un bloc de code, voilà la syntaxe :

@{
int reponse = 42;
}

Cependant, il peut arriver qu’on arriver au scénario inverse que précédemment : un bloc de code C# où on veut insérer autre chose (du Javascript par exemple). Là encore, une autre astuce :

<script type="text/javascript">
@{
    if(valeur > 0)
    {
        @:window.alert('La valeur est supérieure à 0!');
    }
}
</script>

 

Et qui dit bloc du code, dit bloc de commentaires! la syntaxe est sensiblement la même :

@*
Mon bloc de code
*@

 

 

Pour cet article, je me suis inspiré de la génération des vues par Visual Studio, mais aussi de l’article du site Haacked, que j’ai voulu compléter sur certains points. Et si vous voulez aller plus loin sur l’exploration de Razor (savoir comment le moteur fonctionne etc), je vous invite à lire ce ticket de blog de SOAT, rédigé par Léonard LABAT.


Implémentez vos menus Hamburger avec AppShell !

Bonjour à tous!

Dans le cadre du développement d’une application universelle, je voulais une fonctionnalité « bête » mais qui peut vite paraître compliqué si on part from scratch : un menu comme on a sur les applications Microsoft, avec leur fameux hamburgers.

wp_ss_20160615_0002

 

Exemple sur mon téléphone sous WM10 avec l’application Actualités

Cela peut être assez lourd à mettre en place, surtout si on est amené à faire plusieurs applications (passage par un UserControl ?), et après plusieurs recherches je suis tombé par hasard sur un composant qui fait tout le travail pour nous, magnifique n’est-ce pas?

Aperçu et introduction

Avant de parler plus en détail de ce plugin, voyons le rendu sur un projet UWP avec émulation Windows Mobile 10 :

Hamburger1 Hamburger2

 

Ne prêtez pas attention à mes données, c’est juste un mock sur un projet qui vient à peine de démarrer pour montrer l’implémentation du composant 😉

Ce projet a été créé par TommasoScalici sur GitHub, vous pouvez le retrouver sur ce lien. Il est sous licence MIT, et est encore maintenu (dernier commit il y a 26 jours à l’heure où j’écris ces lignes).

Il se base sur un principe simple : le menu hamburger est en fait une page conteneur, qui va contenir notre page principale. C’est cette page principale qui va se charger de tout le reste.

Documentation

L’avantage avec ce composant, c’est que la page d’accueil sur GitHub résume bien les possibilités et l’utilisation du composant, ce qu’on ne retrouve pas forcément chez tous les composants. Donc je ne vais pas m’étendre sur l’implémentation en XAML du composant avec l’architecture du composant, car cela reste assez simple avec la documentation (MenuListItem, Header, Footer, etc.).

Je vais juste revenir sur le point Conteneur/Contenu, au cas où vous êtes amenés à changer vos noms de page/namespaces.

La page conteneur, qui va contenir notre composant AppShell, doit être définie dans le fichier App.xaml.cs, dans la méthode OnLaunched.

if (e.PrelaunchActivated == false)
{
if (rootFrame.Content == null)
{
// Quand la pile de navigation n'est pas restaurée, accédez à la première page,
// puis configurez la nouvelle page en transmettant les informations requises en tant que
// paramètre
rootFrame.Navigate(typeof(MenuPage), e.Arguments);
}
// Vérifiez que la fenêtre actuelle est active
Window.Current.Activate();
}


Ensuite, dans cette page (dans mon cas MenuPage), vous pouvez passer votre page fille dans le constructeur :

///
/// Une page vide peut être utilisée seule ou constituer une page de destination au sein d'un frame.
///
public sealed partial class MenuPage : Page
{
public MenuPage()
{
this.InitializeComponent();
TommasoScalici.AppShell.AppShell.Current.AppFrame.Navigate(typeof(MainPage));
}
}


Et le tour est joué!

Pour résumer, les avantages que j’ai trouvé à ce plugin est :

  • l’implémentation rapide et assez logique
  • le respect des thèmes (dark/light)
  • la disponibilité sur NuGet
  • la documentation ni trop conséquence ni trop succinte
  • le projet toujours actif
  • la licence, non contraignante

En espérant vous avoir aidé et fait connaître un plugin assez sympathique, bon développement!


Implémentation du Pattern Singleton

Le pattern Singleton est l’un des design patterns les plus connus. Il permet de faire en sorte qu’une classse ne possède qu’une seule instance via une méthode de classe retournant celle-ci.
Ce pattern est utilisé pour des classes n’ayant besoin qu’une seule instance, ou encore pour éviter d’utiliser des variables globales dans l’application, ce qui ne fait pas très POO😉 .
Il est vraiment simple à appréhender niveau code : dans notre classe statique Singleton, on a une propriété privée _instance qui ne peut être initialisée que via une méthode GetInstance(). De plus, le constructeur de Singleton est privé, afin que seule la méthode GetInstance puisse y accéder.
public class Singleton
{
	private static Singleton _instance = null;
	private Singleton()
	{
		// Initialisation des propriétés de la classe Singleton
	}
	public static Singleton GetInstance()
	{
		if (_instance == null)
			_instance = new Singleton();
		return _instance;
	}
}
Vous savez désormais implémenter le pattern Singleton. Bon développement!

Erreur 403 sur les Bundles CSS avec ASP.NET MVC sous Azure

Ayant récemment créé un site mettant en avant mon parcours professionnel, j’ai décidé de l’héberger sur la plateforme Azure. Cependant, bien que mon projet soit opérationnel en Debug et en Release en local, une fois déployé il n’y avait tout simplement aucune mise en forme. En fouillant sur Internet, j’ai constaté que je ne suis pas un cas isolé, et que ce problème date de plusieurs années…

En regardant le profiler Edge, je me suis rendu compte que le problème venait du bundle. Après recherche spécifique sur mon moteur de recherche, la solution était là: si votre bundle CSS correspond à un dossier physique de votre projet, IIS va vous renvoyer tout simplement une erreur 403 : étrange, quand on nous incite à déposer des feuilles de style dans un dossier spécifique, et quand on est dans une architecture full Microsoft…

Pour éviter ce désagrément, il suffit juste de remplacer le nom du bundle à sa création :


bundles.Add(new StyleBundle("~/Styles/css").Include(
"~/Content/css/bootstrap.css",
"~/Content/css/bootstrap-responsive.css",
"~/Content/css/site.css"));

ainsi qu’à son appel:


@Styles.Render("~/Styles/css")

C’est ce genre d’erreur qui peut nous prendre des heures à corriger, et heureusement qu’il y a des blogueurs pour relayer l’information. Je tiens ma source de TheBeardDeveloper, qui m’a évité beaucoup de tracas!


Bienvenue !

Bienvenue sur mon nouveau blog/site! 🙂

Voulant mettre davantage l’accent sur le côté blog, je me suis dis qu’un hébergeur « officiel » serait bien (notamment pour les plugins, les images, etc), et qu’abandonner mon site sous Azure pour unifier les deux serait encore mieux. Donc nous voici ici!

Les articles de mon précédent blog seront migrés au fur et à mesure.

Bonne visite!