J'recommence ailleurs, je git mon blog!

Publié par Fabrice Michellonet sous le(s) label(s) le 3 avril 2018

Voila, ça fait maintenant 8 ans que de temps en temps je postais un article ici sur les techno Microsoft.

Malheureusement, blogger n'a plus beaucoup évolué depuis un sacré bon moment et ne match plus trop avec mes attentes.

J'aurais du prendre cette décision il y a déjà plusieurs années, mais cette fois-ci c'est fait, je déménage vers http://www.mymemoryleaks.fr/

On se retrouve la-bas?

Déployer un webjob avec VSTS

Publié par Fabrice Michellonet sous le(s) label(s) , , le 9 février 2018


Si vous avez déjà eu besoin de déployer un azure webjob via vsts, vous avez du tomber sur ce bon blog post de Thomas Hellstrøm. Néanmoins, en suivant telles quelles les instructions de notre ami Thomas, vous risquer de casser la webapp qui host votre webjob.

Ce blog post va prendre la tournure d’une recette de cuisine, d’un pense bête ; cela m’évitera de me reposer la même question dans quelques mois, et j'espère secrètement que cela puisse aider certains d'entre vous.

On commence donc par utiliser la fonction « Publish as Azure Webjob » à partir de Visual Studio 2017.



Pas la peine d’aller au bout du deploy, ce tool va ajouter le package nuget Microsoft.Web.WebJobs.Publish et créer le fichier « webjob-publish-settings.json » dans le répertoire properties de votre projet.

On passe ensuite sur le build sur VSTS. Thomas nous propose de définir les arguments suivants sur la tache msbuild :

/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true 
/p:SkipInvalidConfigurations=true /p:Configuration=Release

Notez le paramètre « /p:WebPublishMethod=Package », qui produira un fichier zip.



Et en jetant un œil dans le fichier zip, mauvaise surprise, on est en train de packager le dossier bin avec des dll qui vont venir écraser celles qui appartiennent à la webapp.



Du coup j’vous propose de corriger ça.
On commence par changer les arguments de la tache msbuild.



/p:DeployOnBuild=true /p:WebPublishMethod=FileSystem 
/p:publishUrl="$(build.artifactstagingdirectory)" /p:DeployDefaultTarget=WebPublish 

Ce qui va produire le même contenu que le fichier zip mais directement sur le système de fichier.

Puis on ajoute une tache delete file, qui se chargera de supprimer le dossier /bin que nous voulons éviter de déployer.




Voila, cette fois ci nous y sommes. J’espère que cela pourra vous aider!

VSTS – Quand y’a plus de crédits, y’en a encore.

Publié par Fabrice Michellonet sous le(s) label(s) , , , le 30 janvier 2018

Ça, c’est la poisse: Your account has no free minutes remaining.

Le scénario est à se pendre, tu montes ton projet en intégration continue et après quelques (dizaines) de commit tu te retrouves dans l’impossibilité de complétera une PR ou juste vérifier que le dernier commit n’a pas introduit de régressions.

Du coup je me propose de présenter ici, comment créer à moindre frais un agent de build sur-vitaminé. Et le fait d’avoir une machine un peu costaud, n’est pas un moindre mal ; qui ne s’est pas arraché les cheveux en attendant que l’agent de build hosté veuille bien se lancer et faire son travail… ?

Plutôt que de créer nous même une vm, installer windows, msbuild et tout le nécessaire a un agent de build, on va plutôt utiliser azure DevTest Lab. L’idée est de tirer parti des template de vm qui sont mises a notre dispo.

Après avoir créé votre première instance DevTest lab, on ajoute une vm basée sur l’image Visual Studio Community 2017 (latest release) on Windows Server 2016 (x64).




On va vous demander un login et un mot de passe pour le compte admin de la future vm.
Je vous conseille de stocker votre mot de passe dans le coffre-fort « my secret » pour une plus grande sécurité. (ce n’est donc pas mon mot de passe que vous voyez 😊)

Bon il nous faut maintenant choisir les caractéristiques de la vm. J’ai opté pour 32 GO de ram et 8 vcpu, une D8S_V3, elle ne sera allumée que quelques minutes le temps de faire le build, donc elle ne nous coutera pas bien cher.

Place aux artefacts, il convient de voir cela comme des scripts additionnels qui installeront d’autres programmes/services sur votre vm. Ça tombe bien, l’artefact VSTS Build agent nous procure le service de build qui convient.




Quelques précisions, le champs VSTS account name doit se conformer au pattern suivant : https://{{vsts_account_name}}.visualstudio.com

Le secret correspond une clé privée que vous pourrez crée en vous rendant à l’adresse https://{{vsts_account_name}}.visualstudio.com/_details/security/tokens

Finalement, l’agent pool doit être un pool pré-existant. A vérifier/créer ici : https://{{vsts_account_name}}.visualstudio.com/_admin/_AgentPool

Petit plus, dans le cas ou vous envisagez de builder des projets front, profitez de cette étape pour ajouter l’artefact NODE JS.

Bon, aller y’a plus qu’a cliquer sur OK, attendre quelques minutes et l’agent devrait apparaitre dans le pool que vous avez choisi.



Quoi qu’il en soit pas de panique, si vous vous êtes trompé sur un paramètre des artefacts, il sera toujours possible de retenter l’installation a postériori ou bien de procéder a l’installation manuellement.

Dernier point, si vos crédits azure ne vous permettent pas de laisser cette vm allumée tout le temps, voici deux petites commandes qui vous permettront d’allumer et d’éteindre la vm a volonté. (a utiliser dans une console azure cli

az lab vm {{command}} --lab-name {{devtestlab}} --name {{vm-name}} 
--resource-group {{resource-group}}
command : start ou stop
devtestlab : le nom de votre instance DevTest lab
vm-name : le nom de votre vm
resource-group : le nom du resource group ou est installé la vm.





OData & ngTable : un duo de choc

Publié par Fabrice Michellonet sous le(s) label(s) , , , le 12 décembre 2016

Dernièrement, j’ai pas mal fait joujou avec OData.
C’est quand même super plaisant de pouvoir requêter une source de données via une URL et avec une richesse proche de ce que l’on connait avec du SQL.
Une fois n’est pas coutume je ne vais pas parler de .NET mais plutôt partager avec vous un bout de javascript qui met en évidence la facilité avec laquelle vous pourrez brancher des tables ngtable avec votre backend odata.

Trêve de bavardage, on passe au code :



Pour cette illustration, j'ai utilisé le web service services.odata.org qui expose la base Northwind (sample Microsoft) que l'on a tous croisés un jour.

Juste un mot sur le code, évidemment c'est le service odataTableService qui fait la glue entre OData et ngTable; c'est lui qui permet de réaliser les filtrages et les sorts. Une fois que ce service est disponible dans votre app, regardez avec quelle simplicité, votre controller peut créer une table :

$scope.tableParams = odataTableService.createTableParams({
      endPoint: 'http://services.odata.org/V3/Northwind/Northwind.svc/Products'
    });

Je me demande vraiment pourquoi OData n'a pas plus que ça le vent en poupe.

Packager un projet SQL pour Octopus

Publié par Fabrice Michellonet sous le(s) label(s) , , le 1 septembre 2016

Que ce soit manuellement ou de manière automatique, lorsque l'on parle de déploiement il va nous falloir définir un package de déploiement.

Dans l'univers Octopus, les packages de déploiement sont des fichiers avec une extension .nupkg. Voyons comment en créer un pour un projet de Base de données.

Ajouter une dépendance vers Octopack


Octopack est l’utilitaire que nous allons utiliser pour générer ce package de déploiement. Démystifions le terme, octopack produit un fichier qui n’est autre qu’un zip, contenant les fichiers à déployer ainsi qu’un ensemble de métadonnées et potentiellement des scripts powershell.
Bref, rien de bien extraordinaire.

Pour l’installer dans notre projet de base de données, nous allons utiliser Nuget, qui est un gestionnaire de dépendances très performant et bien connu des développeurs. Cependant il n’est pas facilement accessible lorsqu’on travailler sur un projet SQL/SSIS/SSRS/SSAS.

Pour tromper Visual Studio nous allons commencer par ajouter un projet C# de type console pour faire simple



Ajoutons sur ce projet C# une dépendance vers Octopack.



Dans la nouvelle fenêtre on cherche le package Octopack, et on l’installe.



Après avoir acquitté la fenêtre de preview d’install, la fenêtre «Output» de visual Studio devrait vous confirmer que l’installation s’est bien passée.



Il aurait été formidable de pouvoir l’installer directement dans notre projet de base, mais nous allons devoir transpirer un peu plus pour arriver à nos fins.
Ready ?? Ouvrons le fichier csproj de l’application console et copions la portion de msbuild qui correspond à Octopack



Puis, copions la discrètement dans notre fichier sqlproj, de la même manière à la fin de celui-ci.
Un petit rebuild pour vérifier que tout fonctionne toujours bien… et TADAM y’a toujours pas de package de déploiement généré.

Créer le package


Bon si vous avez été un peu curieux, vous aurez remarqué que la portion de msbuild que l’on a copiée a pour but d’importer un autre fichier msbuild (qui se trouve dans le répertoire packages\OctoPack.3.4.1\tools\OctoPack.targets)

En baragouinant un peu le msbuild, on comprend assez vite que la variable RunOctoPack est initialisée à false et c’est elle qui déclenche ou non la génération du package.

Alors évidemment, une solution possible est de modifier cette variable à true. Néanmoins cela induit qu’un package de déploiement sera créé a chaque build du projet. Ce n’est vraisemblablement pas ce que vous souhaitez.

Je vous propose plutôt d’ouvrir une console DOS et une fois dans le répertoire du projet, tapez la commande suivante :

msbuild ContinuousBI.Sql.sqlproj /t:Build /p:RunOctoPack=true

Si tout s’est bien passé vous devriez avoir une sortie similaire a celle-ci :



Cette fois ci, si vous jetez un œil dans le sous-dossier obj/octopacked de votre projet vous y découvrirez un fichier .nupkg qui est le package de déploiement

A quoi ressemble mon package ?


Pour aller un peu plus loin nous pouvons jeter un œil à ce qui a été produit pour nous par Octopack.
Je vous propose d’utiliser nuget package explorer
Ouvrez le et glissez déposez le fichier .nupkg, vous devriez obtenir un résultat semblable à celui-ci.



C’est confirmé, le dacpac issu de la compilation à bien été reconnu ; il est embarqué dans package de déploiement.

Nous verrons dans le prochain post comment le déployer avec Octopus.

Prolific usb to ttl et windows 10

Publié par Fabrice Michellonet sous le(s) label(s) , le 11 mai 2016



Dernièrement j'ai ressorti ma BeableBone Black histoire de faire une peu de veille techno. J'vous raconterai un de ces quatre en quoi monter une boite peut vite cramer tout votre temps libre, mais c'est pour la bonne cause.



Bref pour en revenir à la Beagle, je ressors mon câble série que j'utilise pour me connecter à la console. Je fais les branchements et go sur le site de Prolific pour récupérer le dernier driver pour Windows 10.

Tiens! bizarre, même après un reboot je n'ai toujours rien qui s'affiche dans mon terminal.
En creusant, je m'aperçois que le driver du câble n'est pas reconnu.



S'en suit plusieurs tentatives de réinstallation et de reboot infructueuses.

Je cherche un peu sur Internet, pas mal de personnes ont des soucis avec des versions contrefaites acheté sur des sites asiatiques. Le mien vient de chez Adafruit, et il fonctionnait parfaitement il y a quelques mois, sur Windows 8.

Ho... tiens réessayons sur cet ancien PC. Ca fonctionne parfaitement!
Énorme je récupère l'installer du driver qui se trouve sur ce dernier;
Je repasse sur mon PC équipé de Windows 10, désinstalle le driver en place, reboot, installe l'ancien driver (fonctionnant sur Windows 8) et surprise, ca fonctionne.

Si ça c'est pas de l'obsolescence programmée! Honteux!
Rien que pour ça, je vous recommande plutôt d'investir dans un câble concurrent comme ce FTDI. J'en possède un également et fonctionne tout aussi bien.

Du coup, je vous dépose le driver fonctionnel, ici. J'espères que cela pourra aider quelqu'un.

Tiens, en prime je poste ici le schéma de branchement du câble Prolific avec la beagle.
  • Le fil noir va sur le pin 1 (GND)
  • le fil vert sur le pin 4 (RECEIVE)
  • le fil blanc sur le pin 5 (TRANSMIT)
Ne branchez surtout pas le fil rouge, c'est du 5V, vous grilleriez la beagle!!

Comment invoquer une méthode dont on connait le nom à l’exécution sur un dynamic?

Publié par Fabrice Michellonet sous le(s) label(s) , le 24 juillet 2015
Comment invoquer une méthode dont on connait le nom à l'exécution sur un dynamic?

Le mot clé dynamic en C# n'est pas nouveau, il a vu le jour avec C# 4 et pourtant il n’apparaît pas si souvent que ça dans les bouts de code que je fréquente.

Dernièrement je l'ai recroisé en utilisant SignalR; les hubs qui sont générés sont des objets dynamic.

Quoi qu'il en soit, dernièrement j'ai eu besoin d'invoquer une méthode dont le nom ne m'était connu qu'à l'exécution.
Croyez-moi, j'y ai passé un petit moment avant d'y arriver... Cela révèle vraisemblablement ma grande ignorance :)

Ce blog post prendra donc la forme d'un post-it qui me fera gagner du temps la prochaine fois que je me frotterai a cette bête-là. Si pour vous les classes Binder et CallSite n'ont plus de secret ce blog post vous semblera vraisemblablement bien terne.

Posons le problème, on me donne un objet dynamic auquel une méthode est ajoutée à l'exécution

private string sentence = "Hello world";

        public dynamic BuildObj()
        {
            dynamic myDynObj = new ExpandoObject();
            myDynObj.SayHello = new Func<string>(() => sentence);
            return myDynObj;
        }

Si j'avais connaissance de la méthode lors de la compilation je pourrais l'appeler ainsi :

[Test]
        public void CallDirectlyMethod()
        {
            dynamic myObj = BuildObj();

            var ret = myObj.SayHello();
            Assert.That(ret, Is.EqualTo(sentence));
        }

Malheureusement la signature de la méthode ne m'est connue qu'à l'exécution.
Ok, premier réflexe je sors l'attirail de la reflection.... mais les résultats restent catastrophiques :

[Test]
        public void CallMethodWithReflectionFails()
        {
            dynamic myObj = BuildObj();
            Type t = myObj.GetType();
            var method = t.GetMethod("SayHello", BindingFlags.Default | BindingFlags.Instance | BindingFlags.InvokeMethod  | BindingFlags.NonPublic);
            Assert.That(method, Is.Null);
        }

Quels que soient les bindings flags, impossible de trouver la méthode... FAIL!!

Voici, donc la combinaison magique, un savant mélange de Runtime.Binder et CallSite...

[Test]
        public void CallMethodWithCallSite()
        {
            dynamic myObj = BuildObj();

            var callSiteBinder = Binder.InvokeMember(CSharpBinderFlags.None, "SayHello",
                Enumerable.Empty<Type>(), myObj.GetType(),
                new []
                {
                    CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
                });

            var callSite = CallSite<Func<CallSite, object, object>>.Create(callSiteBinder);
            string ret = callSite.Target(callSite, myObj);

            Assert.That(ret, Is.EqualTo(sentence));
        }

Ok, essayons de décrypter un peu ce code qui semble un peu alambiqué.
Binder.InvokeMember va nous permettre à l'exécution de pointer la bonne méthode un peu comme le ferait typeof(myObj).GetMethod()
Le premier paramètre permet de spécifier la manière dont est traité le type de retour. Dans le cas d'une Action nous aurions pu utiliser CSharpBinderFlags.ResultDiscarded. Dans notre cas, une Func, CSharpBinderFlags.None fonctionne bien.

puis viens en seconde position, évidement le nom de la méthode a appelé, passée sous forme de chaine.

En troisième position, il conviendra de fournir la liste des arguments de types à nécessaire dans le cas d'une méthode générique.

Viens ensuite le type sur lequel effectuer l'invocation, ce qui permet de pointer une surcharge.

Et finalement les informations sur les paramètres attendus et le type de retour (le premier élément du tableau);

La création du CallSite<Func<CallSite, object, object>> permet d'arrêter la signature du delegate que l'on cherche à appeler. Notez, que le dernier argument générique "object" devrait etre "string", car il représente le type de retour de la func... mais pour une raison qui m'échappe l'utilisation de string comme type de retour entraîne une erreur au runtime, alors que le retour de la func est bien affecté à une variable string à la ligne du dessous... il faut bien garder un peu de magie :)

Ok, évidemment il ne nous reste plus qu'à effectuer l'invocation, ce qui est réalisé via le code callSite.Target(callSite, myObj);

J'espère que ce post évitera quelques cheveux blancs a ceux qui se trouveront confronté au problème.

BeagleBone Black du carton à Mono

Publié par Fabrice Michellonet sous le(s) label(s) , , , le 1 juin 2015

L’IOT, on en parle partout, de plus en plus ; évidemment l’univers Microsoft n’est pas en retrait sur ce domaine avec la mise à disposition de Windows 10 sur le Raspberry ou encore le MinnowBoard MAX

Vous trouverez pas mal de blog post autour du raspberry… je vais donc plutôt vous présenter comment vous lancer dans le développement .NET embarqu& sur cette magnifique plateforme qu’est le BeagleBone Black (BBB). Au travers de cet article je vous propose d’étudier les différentes étapes à réaliser pour pouvoir lancer votre premier « Hello World » embarqué.



Tout d’abord il n’y a pas de Windows 10 sur le BBB, en tout cas pour l’instant, mais très honnêtement l’installation d’Ubuntu que je vous propose est à la portée de tous.

Matériel nécessaire


Je commence par un petit coup de pub pour le site AdaFruit ou vous trouverez tout le matos que vous pouvez imaginer pour nos petits joujoux.

Aller Go ! Pour faciliter la visualisation de ce qui se passe sur votre BBB je vous conseille d’investir dans un câble Serial TTL-232 en usb. Sans celui-ci vous devrez attendre que le BeagleBone ai récupéré une IP et vous y connecter (en ssh par exemple) et interagir avec.

Pour brancher le câble prenez comme repère le fil noir qui doit venir se placer sur le pin J1 de votre BBB.

Installez les drivers FTDI pour prendre en compte l’adaptateur usb. Ils se trouvent ici

Finalement en utilisant Putty, configuré comme ci-dessous la console s’affiche enfin sur votre pc.



Si l’idée de brancher un câble Ethernet sur votre BeagleBone vous donne de l’urticaire, je vous conseille ce dongle usb. Il est directement reconnu sous Ubuntu.


Installation d’Ubuntu




Nous allons installer dans la mémoire eMMC la version 14.04 d’Ubuntu spécialement conçue pour la BBC. Après avoir téléchargé le fichier img.xz, il vous faudra le décompresser avec un outil comme 7zip.

L’image ainsi extraite pourra être copiée sur une micro sd avec Win 32 Disk Imager

Lorsque la copie se termine, insérer la micro sd dans le port prévu à cet effet sur la BBB. Tout en appuyant avec votre index sur le bouton « boot » (le plus proche du port Ethernet), alimentez la BeagleBone. Laissez le doigt appuyé jusqu’à ce que les diodes clignotent.

Il faut 5 bonnes minutes pour que l’installation se termine ; mais grâce au câble TTL vous serez aux premières loges pour suivre l’installation.

Mise à jour du Système et installation de mono


Ok, si vous en êtes arrivé là, c’est du tout cuit.

Nous allons commencer par mettre à jour le système grâce package manager APT. A partir de la console tapez les commandes suivantes :

sudo apt-get update
sudo apt-get upgrade

Après quelques minutes le système est prêt, on peut passer à Mono. Le runtime devrait être suffisant étant donné que l’on va se contenter d’exécuter des binaires créer sur votre machine de dev. Pour l’installation on tape donc :

sudo apt-get install mono-runtime

Création de l’exécutable et déploiement sur le BBB


Je vous laisse écrire votre premier Hello Beagle World dans Visual Studio ; en créant un projet de type console et en targettant le Framework 4.5;

Une fois compilé, j’ai opté pour un déploiement type « bin-deploy » en utilisant un transfert via PSCP

Une fois PSCP copié, vous pourriez copier le fichier avec une ligne de commande comme celle-ci :

pscp.exe "C:\ BBB\ HelloWorld\bin\Debug\HelloWorld.exe" ubuntu@192.168.10.2:/home/ubuntu

Le premier paramètre étant le ficher à transmettre le second l’url de destination au format :

user@hote:/chemin_de_destination

En dessert, après tous ces efforts, nous pouvons enfin exécuter sur le Beagle notre Hello World en tapant :

Mono HelloWorld.exe

Kibana : Explorez vos données.

Publié par Fabrice Michellonet sous le(s) label(s) , , , le 23 décembre 2014

Cet article fait partie d’une sérié dédiée à Elasticsearch, reportez vous à l’article sommaire pour accéder facilement aux autres posts.


Kibana est la brique de visualisation qui gravite autour d’Elasticsearch. Voici, quelques exemple de représentations faites avec Kibana :





Kibana 2


Kibana 3


L’exploration de données et la réalisation de dashboard ne nécessitent aucun code, ce qui permet de donner la main aux fonctionnels ; De plus Kibana et Elastocsearch permettent d’avoir des rapports real time, se mettant à jour à mesure que les données sont stockées.

Pour les lecteurs qui connaissent bien la stack SQL Serveur, on pourrait le comparer à un Report Builder sous stéroïdes et surtout qui tiendrait (mieux) ses promesses de simplicités et d’accessibilité pour les non-techniciens

Je me propose dans ce billet de découvrir ensemble les fonctionnalités de « data discovering » sur un dataset public recensant les accidents corporels de la circulation

Si ces données vous intéressent, je vous invite d’ailleurs à jeter un œil à la très bonne session (video, slides) de Patrice Harel sur le mining avec SSAS et qui s’appui sur le même jeu de données.


Installation

Commençons par l’installation qui comme pour les autres composants de la stack est triviale :
Tout commence évidement par le téléchargement du binaire ici, suivi de la décompression de l’archive.
Si votre instance elasticsearh ne tourne pas sur le même serveur qui hébergera Kibana ou si elle écoute sur un port non standard, il vous faudra modifier le fichier de configuration config/kibana.yml.
Dans mon cas, je ne touche à rien!
Puis à l’aide d’une fenêtre commande, il nous faudra exécuter /bin/kibana après s’être placé dans le répertoire ou a été décompressé l’archive.
Finalement pointons notre navigateur sur l’url http://localhost:5601

Exploration

S’il s’agit de la première fois que vous lancez Kibana, cet écran vous sera présenté, dans lequel il vous faudra choisir un index (équivalent de base de données) ainsi qu’une colonne typée date.
Ce dernier point est extrêmement important, car Kibana part du principe qu’il est naturel pour l’esprit humain et plus facile de découvrir des données lorsqu’elles sont représentées sur une frise.



Dans notre dataset, nous ne disposons malheureusement pas de la date de l’accident, qui aurait été le candidat idéal en tant que « time field ».
Pour l’exemple, nous nous rabattrons sur le champ @timestamp représentant l’instant ou a été inséré l’événement dans notre index.

On se retrouve propulsé dans nos données via l’écran suivant :



On reconnait très vite plusieurs sections dans cette interface :

  • La colonne de gauche qui contient les champs à disposition (1).
  • Tout en haut (2), une zone de filtre ; n’oublions pas qu’elasticsearch est un moteur de recherche…
  • L’indicateur du nombre d’enregistrements après filtrage (3).
  • La frise temporelle sur laquelle on regarde les événements (4).
  • Finalement la vue tableau de données au centre. Notons que pour l’instant elle nous représente la version brute des données, car nous n’avons pas encore choisit de colonnes à afficher.

Commençons par cliquer sur les barres du graph afin d’effectuer un drilldown au niveau temps.



Instantanément cela nous permet de découvrir la répartition des data dans le temps. Notez, que les clics que nous avons fait sur le graph sont interprétés comme une clause de filtrage, diminuant les événements auxquels on s’intéresse.

Pour avoir un aperçu du contenu des champs, je vous conseille de cliquer sur le nom d'un champ dans la colonne de gauche ; comme ceci :



Les informations et actions présentées ici sont une vraie mine d’or.
En plus d'un premier comptage des données sous-jacentes, on peut par exemple se concentrer sur les événements qui ont eu lieu sur l’A31 en cliquant sur l'icone loupe+. Evidemment, la frise temporelle et les données sont mis à jour.



Maintenant, intéressons nous au champ tbl qui représente le nombre de blessés légers dans un accident.



En cliquant sur « Visualize », on obtient la répartition top 20, sous forme d’histogramme, du nombre de blessés légers sur l’ensemble des événements de la base.



Ici aussi cette visualisation, nous permet d’appréhender rapidement la teneur de nos données.

Finalement jetons un œil, au filtre qui se trouve tout en haut de l’interface de Kibana. Par défaut, vous y trouverez une *. Attention, il n’y a pas ici d’analogie avec SQL, ici l’étoile signifie sans filtre.
Nous pourrions par exemple nous intéresser à une commune en particulier, celle de Cergy par exemple (95002). Il nous suffira pour ce faire de saisir le filtre suivant :

codeinsee:=95002



Ou bien les accidents impliquant plus de 2 morts
ttué:>2



Ou encore les accidents impliquant plus de 2 morts dans l’Oise :
ttué:>2 AND codeinsee [60000 TO 69999]



Voila qui clos ce premier article sur Kibana, en espérant vous avoir donné envie d’essayer ce superbe outil.

Série Elasticsearch

Publié par Fabrice Michellonet sous le(s) label(s) , , , le 10:37

Ce post fait office de sommaire à toute une série d’articles que j’ai bon espoir d’écrire sur cette toute fin d’année 2014 et le début 2015.

Mais pourquoi parler d’Elasticsearch, moi qui ne traite que de technos Microsoft sur ce blog?

Cela part du constat qu’a mon humble avis, la stack BI Microsoft pèche sur deux points :

  • Premièrement, il n’existe pas d’outils permettant une exploration décente des données non encore nettoyées, rangées, retraitées etc.… d’ailleurs, plus la volumétrie est importante et plus ce point est difficilement adressable.
    (i.e : sur de petites volumétries, j’en entends déjà me dire qu’Excel peut avoir ce rôle, avec un enchevêtrement de tableaux croisés dynamiques etc… )
  • L’outillage de reporting est assez pauvre et n’est vraiment pas penser pour être à destination des fonctionnels.
    Je m’explique Reporting Services fait le job lorsqu’il s’agit de réaliser un document pro format, mais nécessite des compétences techniques que nos fonctionnels ne disposent pas ; et c’est bien normal.
    Vous aurez probablement également entendu parler de la version « User Friendly » de « Reporting Services », j’ai nommé Report Builder. La tentative est louable, mais le résultat n’est pas la à hauteur.
    Encore une fois dans les faits, bien peut de fonctionnels ont les connaissances, l’envie et le temps de l’utiliser.

C’est avec ces regrets dans l’âme, que je me retrouve lors de la conférence ncraft 2014 à assister à une superbe session d’Itamar Syn-Hershko consacrée au couple Elasticsearch et Kibana.

Je vous passe les détails de cette présentation, mais effet whaou garanti vers la fin de la session lorsque Kibana rentre en scène.

Certain doivent se dire qu’il m’a fallu presque 1 an pour commencer à sortir un article, quelle productivité !

Pour être tout à fait franc, il y a 1 an il manquait encore quelques features indispensable a mon sens pour que Kibana puisse jouer un rôle aussi important que ce pour quoi il à été conçu. L’exemple le plus probant était l’impossibilité de réaliser un « distinc count » ; un « show stopper » pour qui compte faire un peu de BI sérieusement.

Voila pour le petit historique et mes états d’âmes ; Kibana est arrivé à maturité selon moi dans ses cas d’utilisation standard et cela mérité bien qu’on s’y intéresse ainsi qu’a l’écosystème qui gravite autour.

Voici enfin l’heure du menu :

  • 1ere Partie : Explorez vos données avec Kibana
  • 2eme Partie : Chargez vos données, les yeux fermés avec Logstash
  • 3eme Partie : Introduction à Elasticsearch
  • 4eme Partie : Changement de mapping et Reindexation
  • 5eme Partie : Reporting avancé & Dashbording (Kibana)
  • 6eme Partie : Transformation de données (Logstash)

Il y’a de forte chance que ce sommaire soit agrémenté d’autres articles dans le futur ; le domaine étant extrêmement large.

Dernier point, l’ordre des premiers articles peut vous paraître étrange ; j’ai voulu partir de ce qui apportera le plus de valeur ajouté aux fonctionnels pour finir par ce qui requiers le plus de compétences techniques.

Il ne me reste qu'a vous souhaiter une bonne lecture, et n'hésitez pas a me laisser vos commentaires.

Utiliser Couchbase comme serveur de cache

Publié par Fabrice Michellonet sous le(s) label(s) , , le 11 avril 2014

Dernièrement, je vous présentais une façon simple d'utiliser le cache dans vos applications .NET
Cependant cette solution possède plusieurs inconvénients. En voici quelque uns :

  • Le cache est lié au processus système de votre application (executable / IIS etc...). Si votre programme plante ou est fermé, vous perdez votre cache
  • Si vos besoins en mémoire augmentent, cette solution n'est pas scalable
  • Rien n'est mis a disposition pour monitorer votre cache
  • Vous n'avez pas d'interface de gestion du cache (augmentation de la mémoire allouée etc...)
  • Vous ne pouvez pas invalider le cache sans arrêter l'application.
  • etc...
Bref, vous avez compris, c'est une solution pour démarrer petit.

Aujourd'hui, je voudrais partager avec vous une solution qui répond à tous les manques évoqués plus haut et plus encore.
Couchbase est une base NoSQL Key/Value; la value ayant comme particularité d'être un document json.
Couchbase est issu du mariage heureux de Membase (cache mémoire avec réplication des caches, persistance et interface d’admin Web sexy) et
CouchDB (NoSQL orienté document). Ce qui nous intéresse plus particulièrement aujourd'hui sont les fonctionnalités qui en font un bon serveur de cache.


Installation


Pour l'installer, tout commence par télécharger le setup ici

Je vous passe le scénario d'installation qui a le bon gout de se faire en "Next" > "Next" > "Next"

Passons à la configuration :

Dans la première étape, il vous faudra spécifier l'emplacement des fichiers (base et index) ainsi que la mémoire dévolue à Couchbase.

configure server

Un peu plus loin nous allons créer le l'espace qui accueillera les objets que l'on va mettre en cache (bucket).

bucket

Quelques instant plus tard vous voila avec un Couchbase flambant neuf sur votre machine.

up and running

Note : si la page ne s'ouvre pas automatiquement dans votre browser, la console d'admin de couchbase se trouve a l'adresse suivante : http://localhost:8091/


Implementation


Couchbase dispose d'un très bel écosystème qui gravite autour, et la plateforme .NET ne fait pas exception.
Je me propose d'adapter le cache manager vu dans le précédent article.
Pour rappel nous avions l'interface suivante :


public interface ICacheManager
{
    T GetOrInsert<T>(string key, Func<T> callback) where T : class;
}

Passons à l'implémentation. Il va nous falloir un driver, que l'on peut aisément installer via nuget :



son utilisation est des plus simple, avec des méthodes explicites comme Get() et Store().
Voici une implémentation de cache respectant notre interface basée sur Couchbase.


public class CouchbaseCacheManager : ICacheManager
    {

        private readonly CouchbaseClient _client;

        public CouchebaseCacheManager()
        {
            _client = new CouchbaseClient();
        }

        public T GetOrInsert<T>(string key, Func<T> callback) where T : class
        {
            T elmnt;
            try
            {
                if (_client.KeyExists(key))
                    return _client.Get<T>(key);
                elmnt = callback();
                _client.Store(StoreMode.Add, key, elmnt);
            }
            catch (Exception)
            {
                elmnt = callback();
            }
            return elmnt;
        }
    }

Et vous, quel type de cache utilisez vous dans vos projets?

Poor man's cache

Publié par Fabrice Michellonet sous le(s) label(s) , , le 4 avril 2014

Depuis la version 4 du framework .NET, la gestion d'un cache est profondement encrée dans l'adn de notre framework.
Il devient alors extrêmement facile de stocker en mémoire à peu près n'importe quoi.
Je vous livre dans ce court billet, une implémentation extrêmement simple que j'utilise souvent dans mes projets.
Commençons par définir une interface, qui nous permettra d'injecter notre cache manager un peu partout :

public interface ICacheManager
{
    T GetOrInsert<T>(string key, Func<T> callback) where T : class;
}

et finalement voici une implémentation extra simple de notre cache manager

public class CacheManager : ICacheManager
{
    public T GetOrInsert<T>(string key, Func<T> callback) where T : class
    {
        ObjectCache cache = MemoryCache.Default;
        if (cache.Contains(key))
            return cache[key] as T;
        T elmnt = callback();
        cache.Add(new CacheItem(key, elmnt), new CacheItemPolicy());
        return elmnt;
    }
}

Voici un exemple d'utilisation.

IEnumerable<Product> products = _cache.GetOrInsert("Ma_Clef_Unique_Cache", 
() => { return _productService.GetFrenchProducts(); } );

Simple non?

IISExpress : Servir des pages vers l’extérieur.

Publié par Fabrice Michellonet sous le(s) label(s) , le 28 mars 2014

Nul doute, IISExpress nous a simplifié la vie au quotidien de par sa facilité d'utilisation et son intégration à Visual Studio.
Aujourd'hui, je devais travailler à rendre un site compatible avec l'ipad. Ni une ni deux, je lance safari et pointe vers l'ip de ma machine de dev.
Et boom une belle erreur, me prévient que IISExpress ne servira de pages qu'en local.

Voici comment je l'ai configuré afin de pouvoir tester sur mon Ipad.

1 - Configurons notre site web afin qu'il soit bindé sur toutes les IP de notre machine de dev.

Pour ce faire ouvrons le fichier de config de IISExpress qui se trouve sur : %userprofile%\My Documents\IISExpress\config\applicationhost.config
Recherchons la config de notre site


<site name="MonSite" id="10">
<application path="/" applicationPool="Clr4IntegratedAppPool">
<virtualDirectory path="/" physicalPath="C:\!Project\MonSite" />
</application>
<bindings>
<binding protocol="https" bindingInformation="*:44300:localhost" />
</bindings>
</site>

remplaçons le binding afin que le site soit servi quelque soit l'ip sur laquelle arrive la requete


<site name="MonSite" id="10">
<application path="/" applicationPool="Clr4IntegratedAppPool">
<virtualDirectory path="/" physicalPath="C:\!Project\MonSite" />
</application>
<bindings>
<binding protocol="https" bindingInformation="*:44300:*" />
</bindings>
</site>

2 - Réservons l'url au niveau du kernel (HTTP.sys)

Cela se fait via la ligne de commande suivante, a executée dans une console en mode admin.


netsh http add urlacl url=https://*:44300/ user=everyone

Note : pour ceux qui travaillent sur un windows en français il faudra alors taper


netsh http add urlacl url=https://*:44300/ user="tout le monde"


3 - Autoriser le trafic au travers du firewall

De nouveau une commande à exécuter via une console en mode admin :


netsh firewall add portopening TCP 44300 IISExpressWeb enable ALL


Voila, j'espère que cette astuce pourra vous éviter de rester coincé si vous devez exposer un site hebergé sur votre machine de dev

EntityFramework.Patterns 0.8 est compatible EF 6

Publié par Fabrice Michellonet sous le(s) label(s) , , , le 17 novembre 2013

Ça faisait longtemps que je n'avais pas remis les mains dans ce projet, plus d'un an pour être exact. Le passage à Entity Framework 5 n'avait pas posé de soucis car aucun breaking changes dans l'API n'avait bloqué l'utilisation de la librairie. C'est surement ce qui explique le nombre "plutôt important" (7500) download sur nuget gallery.

Mais avec EF 6 c'est une autre histoire, tout un tas de nouvelles fonctionnalités très sympa, mais aussi une api qui a pas mal bougé... ainsi plusieurs utilisateurs sur codeplex, m'ont tout naturellement contacté pour me demander de mettre à jour EntityFramework.Patterns.

C'est chose faite, la version 0.8 est désormais disponible via nuget ou sous forme binaire et est compatible avec la dernière version d'EF

Ça signifie que mes chers utilisateurs vont pouvoir profiter des gains de performances apportées par EF 6; Quant au projet sur lequel je travaille actuellement, j'espère bien upgrader EF dès demain... Romain, si tu m'entends :p

Dans un avenir proche, je pense ajouter un bout d'API asynchrone aux repository d'EntityFramework.Patterns.

Bon upgrade de package !!

SSIS vs TPL.Dataflow

Publié par Fabrice Michellonet sous le(s) label(s) , , , le 31 octobre 2013

Il y a quelques jours je tweetais de joie, après être tombé sur la msdn concernant TPL.Dataflow
L'API propose un système de pipeline que l'on peut aisément imaginer utiliser dans un process d'ETL.

Ni une ni deux, je m'imagine délaisser SSII lors de mes prochaines alims de base de données et enfin pouvoir unit tester mon ETL.
L'accueil fait par mes collègues purement BI fut plutôt frileux... n'est ce pas David :)


Au rayon des réponses constructives, Fred Brossard, me fait revenir très vite sur terre, en demandant à voir les perfs du framework.

C'est ce que je me propose de présenter ici avec un exemple simple.
L'idée est d'importer le snapshot de septembre des posts de stackoverflow; d'abord avec notre bon vieux SSIS puis avec TPL.Dataflow.

En entrée on a donc un bon gros fichier XML de 170MO correspondant aux nouveaux posts du mois.

Commençons par SSIS, rien de sorcier, une XML source qui se déverse directement dans une SQL Destination pointant vers ma jolie table.



Ce qui nous donne env. 150K lignes intégrées en env. 20 sec



Ok, passons à la version C#. Ce que je vous livre ici est une version non optimisée et naive d'un algo d'import avec TPL.Dataflow.

Commencons par créer nos taches :

// XML datasource 
            var inputBlock = new BufferBlock<XDocument>();

            // Split xml, one for each <row> element
            var xDocumentToXmlRow = new TransformManyBlock<XDocument, XElement>(
                x => x.Root.DescendantNodes().OfType<XElement>());

            // Convert xml <row> element to a Post object.
            var xmlRowToObjectBlock = new TransformBlock<XElement, Post>(
                x => x.FromXElement<Post>(),
                new ExecutionDataflowBlockOptions {MaxDegreeOfParallelism = DataflowBlockOptions.Unbounded});

            // a buffer of 10000 elements
            var batch = new BatchBlock<Post>(10000);

            // SQL destination, using bulk insert.
            var sqlBulk = new ActionBlock<Post[]>(x =>
                {
                    using (SqlBulkCopy sbc = new SqlBulkCopy("Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=Stackoverflow;Data Source=(local)", SqlBulkCopyOptions.TableLock))
                    {
                        sbc.DestinationTableName = "Posts";

                        // Number of records to be processed in one go
                        sbc.BatchSize = 10000;

                        // commit to the server
                        sbc.WriteToServer(x.ToList().ToDataTable());
                        sbc.Close();
                    }
                },
            new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = DataflowBlockOptions.Unbounded });

Il nous faut maintenant ordonnancer ces taches, comme nous le faisons dans SSIS en reliant les taches entre elles.

using (inputBlock.LinkTo(xDocumentToXmlRow))
            using (xDocumentToXmlRow.LinkTo(xmlRowToObjectBlock))
            using (xmlRowToObjectBlock.LinkTo(batch))
            using (batch.LinkTo(sqlBulk))
            {
                inputBlock.Completion.ContinueWith(t => xDocumentToXmlRow.Complete());
                xDocumentToXmlRow.Completion.ContinueWith(t => xmlRowToObjectBlock.Complete());
                xmlRowToObjectBlock.Completion.ContinueWith(t => batch.Complete());
                batch.Completion.ContinueWith(t => sqlBulk.Complete());

                var inputfile = XDocument.Load(@"C:\!PoC\Stackoverflow\Sourcefiles\Posts.xml");
                inputBlock.Post(inputfile);

                inputBlock.Complete();
                sqlBulk.Completion.Wait();
            }


Aller, sans plus attendre, voici le résultat de l’exécution :



Je suis sur que vous êtes aussi étonnés que moi... TPL est plus rapide (de quelques 15%) par rapport à SSIS dans ce cas simple.
Pour être tout à fait franc, je ne m'y attendais pas du tout. Je pensais qu'il y aurait vraisemblablement une 20 aine de % d'écart, mais en faveur de SSIS.

Bien que cet exemple simplissime ne puisse pas permettre de généraliser, TPL.Dataflow semble apporter des performances suffisantes, si non meilleures qu'SSIS;
mais surtout, cet API va nous permettre d’écrire des tests unitaires sur nos jobs d'intégration. C'est ce que je vous présenterai dans un prochain article.


Et vous, avez vous déjà utilisé TPL.Dataflow? Pourriez-vous l'utiliser dans vos projets? A la lumière de ce post, que pensez vous de ces performances?

Entity Framework Cache

Publié par Fabrice Michellonet sous le(s) label(s) le 5 septembre 2012

Malgré la version 5 d'Entity Framework sortie il y a peu de temps, beaucoup déplorent l’absence d’un mécanisme de cache implémenté dans le Framework.

Pour ceux qui ont un peu creusé le sujet, il y a bien les "Tracing and Caching Provider Wrappers for Entity Framework" parus début 2011. Cependant, ce n’est pas la solution ultime ; à l’époque Code First n’existait pas ce qui rend cette librairie incompatible avec Code First.

Pour être tout à fait franc, le code des wrappers est disponible, je pense donc qu’en bidouillant un peu, ça doit être possible d’adapter le code pour Code First.

A l’occasion de la sortie d’EntityFramework.Patterns 0.7, je vous propose de voir ensemble comme utiliser les fonctionnalités de cache fournies par cette librairie.

Soit le contexte suivant :

    public class EfContext : DbContext
    {
        public DbSet<User> Users { get; set; }
 
        public EfContext()
        {
            Database.SetInitializer(new DropCreateDatabaseIfModelChanges<EfContext>());
        }
    }

Et l’entité User tel que :

    public class User
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public virtual ICollection<UserRole> UserRoles { get; set; }  
    }

On peut facilement utiliser le cache en mémoire (il est possible d’en utiliser d’autre) fournit par EntityFramework.Patterns pour mettre en cache une collection de User.

            // On charge tous les users et on les stocke dans le cache.
            using (EfContext ctx = new EfContext())
            {
                IEnumerable<User> usrs = ctx.Users.ToCache();
            }
 
            // autre code...
 
            // Cette fois ci, on récupère les users à partir du cache.
            using (EfContext ctx = new EfContext())
            {
                IEnumerable<User> usrs = ctx.Users.FromCache();
            }

Simple non ?

Pour ceux qui préfèrent éviter l’accès direct au context d’Entity Framework et qui isolent leur accès à la base dans des repository, EntityFramework.Patterns vous facilitera la tâche également.

Il vous suffit de wrapper votre Repository avec le CacheableRepository qui vous apporte les fonctionnalités de cache.

            using (EfContext ctx = new EfContext())
            {
                DbContextAdapter adpt = new DbContextAdapter(ctx);
 
                IRepository<User> repo =
                    new CacheableRepository<User>(
                        new Repository<User>(adpt)
                        );
 
                // On charge tous les utilisateurs.
                // Par defaut le CacheableRepository va mettre en cache le resultset
                // en provenance de la base et le réutilisera le cas échéant plus tard.
                IEnumerable<User> usrs = repo.GetAll();
 
                // autre code.
 
                // Cette fois-ci, les users proviennent du cache. Il n'y a pas d'accès en base.
                usrs = repo.GetAll();
            }

Comme vous le voyez le CacheableRepository fait appel au cache pour vous ; et vous permet de ne pas avoir à gérer la suppression d’éléments dans le cache lorsque les éléments ont étés modifiés/supprimés/insérés etc…

Je reviendrais ultérieurement dans un prochain post sur les mécanismes avancés du cache proposés par EntityFramework.Patterns.

Implémenter une sécurité à base de rôles avec Windows Authentication & SQL Server

Publié par Fabrice Michellonet sous le(s) label(s) le 14 octobre 2011

Ces derniers jours j'ai été confronté à une problématique très intéressante; j'aimerais la partager avec vous aujourd'hui et vous soumettre la solution que j'ai imaginé pour y répondre.

Mon client souhaitait restreindre l'accès à son site/application intranet aux seuls membres d'un groupe Windows prédéfini.
Le hic est que le site fonctionne avec des rôles définis en base de données, laissant ainsi libre champs aux admins de l'appli d'attribuer et révoquer des droits aux autres utilisateurs. Évidement, il était hors de question de devoir passer par l'IT pour changer les droits des utilisateurs à l'avenir.

Si l'on transpose la demande en termes techniques, on a besoin du WindowsTokenRoleProvider pour autoriser l'accès au site et pour tout le reste il faut se reposer sur un SqlRoleProvider.
Comme vous le savez, ce genre de configuration n'est pas prise en charge par le modèle de provider tel que nous le connaissons jusqu'à présent.

En cherchant sur la toile si ce genre de situation à déjà été traitée (ne réinventons pas la roue carrée), vous tomberez très certainement sur un article de Scott Guthrie qui traite d'un problème très similaire. Dans l'article l'homme à la chemise rouge nous montre comment, sans code, utiliser les rôles issus de la base de données et n'autoriser l'accès au site qu'aux utilisateurs identifiés. Et c'est bien sur ce dernier point que nos problématiques diffèrent car dans mon cas, il ne suffit pas d'être identifié sur le domaine, mais il faut aussi faire partie d'un groupe d'utilisateurs.

J'ai donc remonté mes manches, et quitte à écrire du code j'ai tenté de trouver une solution générique permettant de combiner des RoleProvider.

Commençons par définir un RoleProviderDecorator qui comme son nom l'indique suit le pattern Decorator.
public class RoleProviderDecorator<TSurrogateRoleProvider> : RoleProvider
        where TSurrogateRoleProvider : RoleProvider, new()
    {

        protected TSurrogateRoleProvider _surrogate;

        public override void Initialize(string name, NameValueCollection config)
        {
            if (config == null)
                throw new ArgumentNullException("config");

            base.Initialize(name, config);

            _surrogate = new TSurrogateRoleProvider();

            InitializeInnerProvider(_surrogate, name, config);
        }

        protected virtual void InitializeInnerProvider(RoleProvider innerProvider, string name, NameValueCollection config)
        {
            innerProvider.Initialize(name, config);
        }
        
        public override bool IsUserInRole(string username, string roleName)
        {
            return _surrogate.IsUserInRole(username, roleName);
        }
        
        public override string[] GetRolesForUser(string username)
        {
            return _surrogate.GetRolesForUser(username).ToArray();
        }
        
        public override void CreateRole(string roleName)
        {
            _surrogate.CreateRole(roleName);
        }
        
        public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
        {
            return _surrogate.DeleteRole(roleName, throwOnPopulatedRole);
        }
        
        public override bool RoleExists(string roleName)
        {
            return _surrogate.RoleExists(roleName);
        }
        
        public override void AddUsersToRoles(string[] usernames, string[] roleNames)
        {
            _surrogate.AddUsersToRoles(usernames, roleNames);
        }
        
        public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
        {
            _surrogate.RemoveUsersFromRoles(usernames, roleNames);
        }
        
        public override string[] GetUsersInRole(string roleName)
        {
            return _surrogate.GetUsersInRole(roleName).ToArray();
        }
        
        public override string[] GetAllRoles()
        {
            return _surrogate.GetAllRoles().ToArray();
        }
        
        public override string[] FindUsersInRole(string roleName, string usernameToMatch)
        {
            return
                _surrogate.FindUsersInRole(roleName, usernameToMatch).ToArray();
        }
        
        public override string ApplicationName
        {
            get { return _surrogate.ApplicationName; }
            set { _surrogate.ApplicationName = value; }
        }
    }

L'idée du décorateur, vous l'aurez compris, est de déléguer l’exécution des méthodes au vrai RoleProvider passé sous forme de Generic.

Maintenant définissons un RoleProvider qui se charge d'utiliser de manière sous-jacente deux RoleProvider :

public abstract class UnionRoleProvider<TPrimaryRoleProvider, TSecondaryRoleProvider> : RoleProviderDecorator<TPrimaryRoleProvider>
        where TPrimaryRoleProvider : RoleProvider, new()
        where TSecondaryRoleProvider : RoleProvider, new()
    {
        private TSecondaryRoleProvider _secondSurrogate;

        public override void Initialize(string name, NameValueCollection config)
        {
            if (config == null)
                throw new ArgumentNullException("config");

            base.Initialize(name, config);

            _secondSurrogate = new TSecondaryRoleProvider();
            InitializeInnerProvider(_secondSurrogate, name, config);
        }

        public override bool IsUserInRole(string username, string roleName)
        {
            return _surrogate.IsUserInRole(username, roleName) || _secondSurrogate.IsUserInRole(username, roleName);
        }

        public override string[] GetRolesForUser(string username)
        {
            return _surrogate.GetRolesForUser(username).Union(_secondSurrogate.GetRolesForUser(username)).ToArray();
        }

        public override void CreateRole(string roleName)
        {
            _surrogate.CreateRole(roleName);
            _secondSurrogate.CreateRole(roleName);
        }

        public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
        {
            return _surrogate.DeleteRole(roleName, throwOnPopulatedRole) && _surrogate.DeleteRole(roleName, throwOnPopulatedRole);
        }

        public override bool RoleExists(string roleName)
        {
            return _surrogate.RoleExists(roleName) || _secondSurrogate.RoleExists(roleName);
        }

        public override void AddUsersToRoles(string[] usernames, string[] roleNames)
        {
            _surrogate.AddUsersToRoles(usernames, roleNames);
            _secondSurrogate.AddUsersToRoles(usernames, roleNames);
        }

        public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
        {
            _surrogate.RemoveUsersFromRoles(usernames, roleNames);
            _secondSurrogate.RemoveUsersFromRoles(usernames, roleNames);
        }

        public override string[] GetUsersInRole(string roleName)
        {
            return _surrogate.GetUsersInRole(roleName).Union(_secondSurrogate.GetUsersInRole(roleName)).ToArray();
        }

        public override string[] GetAllRoles()
        {
            return _surrogate.GetAllRoles().Union(_secondSurrogate.GetAllRoles()).ToArray();
        }

        public override string[] FindUsersInRole(string roleName, string usernameToMatch)
        {
            return
                _surrogate.FindUsersInRole(roleName, usernameToMatch)
                    .Union(_secondSurrogate.FindUsersInRole(roleName, usernameToMatch))
                    .ToArray();
        }
    }

Dans mon cas, seul un des deux RoleProvider doit pouvoir écrire dans son médium de stockage.
L'autre doit être en lecture seule. Pour ce faire, j'introduis un ReadOnlyRoleProvider :

public class ReadOnlyRoleProvider<TSurrogateRoleProvider> : RoleProviderDecorator<TSurrogateRoleProvider>
        where TSurrogateRoleProvider : RoleProvider, new()
    {

        public override void CreateRole(string roleName){}

        public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
        {
            return true;
        }

        public override void AddUsersToRoles(string[] usernames, string[] roleNames){}

        public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames){}

Ça y est on a fait 90% du boulot.

Dans le fichier web.config on veut écrire ceci :

<roleManager enabled="true" defaultProvider="MyRoleProvider">
      <providers>
        <clear/>
        <add name="MyRoleProvider " type="Providers.MyRoleProvider " connectionStringName="cnx" applicationName="MyApp" />
      </providers>
    </roleManager>

Étant donné que l'on ne peut pas utiliser de classe générique dans la config, on va simplement en spécialiser une :

public class MyRoleProvider : UnionRoleProvider<SqlRoleProvider, SilentWindowsTokenRoleProvider<WindowsTokenRoleProvider>>
    {

        protected override void InitializeInnerProvider(RoleProvider innerProvider, string name, NameValueCollection config)
        {
            NameValueCollection cfg = config;
            if (innerProvider is ReadOnlyRoleProvider<WindowsTokenRoleProvider>)
            {
                cfg = new NameValueCollection(config);
                cfg.Remove("connectionStringName");
            }
            
            base.InitializeInnerProvider(innerProvider, name, cfg);
        }
    }

Ouf, 99% du boulot. Vous pouvez maintenant exécuter et ça va fonctionner... jusqu'à ce que vous utilisiez une des méthodes suivantes :
  • GetUsersInRole
  • GetAllRoles
  • FindUsersInRole

Tout simplement parce que le WindowsTokenRoleProvider ne supporte pas ces opérations.
Ok, voici le tout dernier % à réaliser :

public class SilentWindowsTokenRoleProvider<TSurrogateRoleProvider> : ReadOnlyRoleProvider<TSurrogateRoleProvider>
        where TSurrogateRoleProvider : RoleProvider, new()
    {

        public override string[] GetUsersInRole(string roleName)
        {
            return new string[]{};
        }

        public override string[] GetAllRoles()
        {
            return new string[]{};
        }

        public override string[] FindUsersInRole(string roleName, string usernameToMatch)
        {
            return new string[]{};
        }
    }

Voila 100% de la solution; beaucoup de code aujourd'hui, j'espère que ça ne vous a pas trop démotivé.

Si vous avez une solution autre, ou contestez mon approche n'hésitez pas à laisser un commentaire.

Entity Framework et AOP

Publié par Fabrice Michellonet sous le(s) label(s) , , , le 7 octobre 2011

Cela fait des mois que je repousse la publication d'un post sur l'AOP, car je ne voulais pas vous resservir le sempiternel exemple de mise en place d'une gestion de log applicatif simplifiée.
Si vous voulez vous rafraichir les idées sur le sujet je vous conseille de jeter un œil sur l'article d'Ayende Rahien sur le sujet

Avec le framework .NET, il existe au moins 6 façons différentes d'ajouter un brin d'AOP dans vos programmes;
Pour mémoire il s'agit de :
  • Remoting Proxies
  • Dériver votre classe de ContextBoundObject
  • Passer par un dynamique proxy ( ex : Castle Dynamic Proxy)
  • Utiliser l'API de profiling de .NET
  • Injection d'IL après compilation
  • Injection d'IL au runtime

Dans cet article on s'intéressera particulièrement a l'injection post compilation, tout simplement car c'est la plus performante (le code lié à l'aspect est directement inscrit dans l'assembly finale et rien ne le distingue du reste du code) et aussi car c'est la façon la plus sexy a mon gout de faire de l'AOP (c'est une raison comme une autre, non?).

Bon revenons à nos moutons.

Sur presque tous les projets sur lesquels je suis intervenu ces dernières années, lorsqu'on modélise les entités qui devront être persistées en base, on leur adjoint au moins quatre propriétés :
  • Created By (string)
  • Created (datetime)
  • Last Updated By (string)
  • Last Updated (datetime)

J'ai pour habitude de nommer cette construction, une entité "Auditable", ce qui se traduit en code par l'interface suivante :
public interface IAuditable
    {
        string CreatedBy { get; set; }
        DateTime Created { get; set; }
        string UpdatedBy { get; set; }
        DateTime? Updated { get; set; }
    }

Vous l'aurez compris, l'idée ici, est de stocker la date et l'utilisateur ayant créé ou modifié l'entité en question et ceci a chaque accès base.
Je vous laisse imaginer le travail rébarbatif que cela peut vite devenir si l'on doit tout gérer à la main et si notre modèle est composé de dizaines voire de centaines d'entités.

Je vous propose donc une idée afin de se faciliter la vie grâce à l'AOP.
Il est à noter que bien que l'exemple ci-dessous s’appuie sur Entity Framework, le mécanisme est très certainement transposable (avec adaptation) aux autres ORM.

On commence par utiliser Entity Framework Code First, et on ajoute notre framework AOP préféré
Install-Package EntityFramework
Install-Package Afterthought

En attendant qu'un de mes patch soit accepté et intégré à Afterthought, il vous faudra remplacer la dll d'Afterthought par la mienne disponible ici.

Nuget a rajouté quelques dll et références dans votre projet et modifié également le post build event de votre projet;
Désormais à chaque compilation, Afterthought scannera les assemblies à la recherche de taches d'injection d'IL à effectuer.

Voyons comment demander à Afterthought d'injecter l'interface IAuditable sur nos entités.

public class AuditableAmender<T> : Amendment<T, T>
    {

        public AuditableAmender()
        {
            Properties.Add<string>("CreatedBy");
            Properties.Add<DateTime>("Created");
            Properties.Add<string>("UpdatedBy");
            Properties.Add<DateTime?>("Updated");

            Implement<IAuditable>();
        }
    }

Le code me semble assez clair sans avoir a revenir longuement dessus; On demande simplement à Afterthought d'injecter les propriétés nécessaires à l'implémentation de l'interface IAuditable.

Bien créons un attribut de marquage, que nous placerons sur nos entités :

[AttributeUsage(AttributeTargets.Class)]
    public class AuditableAttribute : Attribute { }

il nous reste encore deux tâches à réaliser;
  • Préciser les assemblies à introspecter
  • Permettre à Afterthought de découvrir les classes qui doivent être modifiées et surtout comment.
Cela se fait dans une même classe :

[AttributeUsage(AttributeTargets.Assembly)]
    public class AmendAttribute : Attribute, IAmendmentAttribute
    {
        IEnumerable<ITypeAmendment> IAmendmentAttribute.GetAmendments(Type target)
        {
            if (target.GetCustomAttributes(typeof(AuditableAttribute), true).Length > 0)
            {
                ConstructorInfo constructorInfo = typeof(AuditableAmender<>).MakeGenericType(target).GetConstructor(Type.EmptyTypes);
                if (constructorInfo != null)
                    yield return (ITypeAmendment)constructorInfo.Invoke(new object[0]);
            }
        }
    }
En clair, pour chaque classe qui implémente IAuditable, on va faire appel à la classe AuditableAmender (créée précédemment) pour modifier la classe.
En appliquant cet attribut sur l'assembly qui contient vos entités, Afterthought effectuera son travail d'injection.

Désormais si l'on applique l'attribut Auditable sur une de nos entités comme suit :
[Auditable]
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

    public class Context : DbContext
    {
        public virtual DbSet<Product> Products { get; set; }

        public Context()
        {
            Database.SetInitializer(new DropCreateDatabaseIfModelChanges<Context>());
        }
    }

Ce qui produit bien en base la table suivante :

table

et dans l'assembly finale (vu avec Reflector)

assembly

Évidemment, en généralisant ce principe il est possible de facilement faire évoluer votre modèle sans avoir à travailler parfois de manière répétitive.

Vous retrouverez cette fonctionnalité (il vous suffit de marquer votre entité avec l'attribut) dans la librairie EntityFramework.Patterns que je maintiens et disponible via nuget.

Que pensez-vous de cette technique?
Utilisez-vous un autre framework (Postsharp) pour réaliser ce genre de tâche?

Live charting avec SignalR

Publié par Fabrice Michellonet sous le(s) label(s) , , , le 26 septembre 2011

SignalR is awesome ! Oui il va falloir vous habituer à entendre dire que cette librairie est un petit bijou.


tweet signalr

Mais qu'est-ce qu'elle a de si bien cette librairie SignalR ?

SignalR fournit une couche d'abstraction au-dessus des WebSocket et des long polling connections pour les développeurs .NET et fonctionne sous IIS sans avoir à installer un autre service à côté.

Jusqu'alors il existait bien des solutions commerciales de ces technos dans l'écosystème .NET mais aucune n'avait vraiment convaincu, souvent par manque d'intégration forte entre le client et le serveur; rendant la communication entre les deux fastidieuse.
Avec SignalR c'est tout le contraire, le code serveur et le code client ne semble faire qu'un. Les lignes de code glissent du serveur vers le navigateur, sans aucune lourdeur. C'est SIMPLE.

Comme d'hab, rien de mieux qu'un petit bout de code pour démontrer ça.

Bon, si vous avez lu le titre vous avez une petite idée de ce que l'on va coder...

Coté composants techniques, évidement SignalR pour la communication client/serveur et Highchart coté client pour dessiner le chart.

Après avoir créé un nouveau projet WebForm ou MVC ajoutez le package SignalR via nuget.
Intéressons-nous au code javascript. (A noter que le code lié à la configuration du chart à été omis a des fins de clarté.)

$(function () {
     var stockExchangeServer = $.connection.stockExchange;

    stockExchangeServer.drawShareValue = function (id, content) {

        if (id != this.Id) {
            return;
        }

        // set up the updating of the chart.
        var series = chart.series[0];
        var x = new Date(parseInt(content[0].Date.substr(6))).getTime(),
                y = content[0].Price;

        series.addPoint([x, y], true, true);
    };

    $.connection.hub.start(function () {
        stockExchangeServer.connect();
            .done(function (success) {
                if (success === false) {
                    console.log(":(");
                }
                console.log("connected");
            });
    });
});

Pour initier la communication avec le serveur, il suffit comme vous pouvez le voir d'utiliser la méthode $.connection.hub.start();
On se contente alors de faire appel à la méthode connect; nous implémenterons cette dernière côté serveur.... oui vous avez bien lu, côté serveur.

Remarquez également la définition de la méthode drawShareValue() qui se charge de dessiner un nouveau point dans le chart, nous ferons appel à elle dans le code serveur.


Côté serveur, nous allons définir un hub, la classe de base qui abstrait la communication client/serveur dans l'api SignalR.

public class StockExchange : Hub
    {
        private Timer _timer;

        private double _fowlerPrice = 10.2;

        private double VaryPrice()
        {
            Random rnd = new Random();
            _fowlerPrice = _fowlerPrice + (rnd.Next(-1, 2) * rnd.NextDouble());
            return _fowlerPrice;
        }

        public bool Connect()
        {
            // Set unique id for client.
            Caller.Id = Context.ClientId;

            _timer = new Timer { Interval = 1000 };

            _timer.Elapsed += (sender, e) => Send(
                new List<SharePrice>(new[]
                                         {
                                             new SharePrice
                                                 {
                                                     Date = DateTime.Now,
                                                     Price = VaryPrice(),
                                                     Share = new Share {Name = "Fowler-Corp"}
                                                 }
                                         }));
            _timer.Start();

            return true;
        }

        public void Send(IEnumerable<SharePrice> sp)
        {
            Clients.drawShareValue(Context.ClientId, sp);
        }
    }

Avez-vous remarqué la méthode Connect()? On y a fait appel à partir du code JS.
Et l'appel à drawShareValue(), fait bien référence à la méthode que l'on à définit dans le javascript. Enorme, non?

Tout cela est possible parce que SignalR utilise dans ses fondements le typage dynamic à laquelle vient s'ajouter une résolution de Propriété/Méthode "simple" par convention, évitant ainsi d'avoir à écrire du code verbeux.

Au final, voici ce que l'on peut obtenir...

chart

Bon ok, l'image ne bouge pas, il faut imaginer qu'un point vient s'ajouter chaque seconde dans le graph :)

Pour ceux qui voudrait jeter un coup d’œil à la solution entière, vous pouvez la récupérer sur Github

Avez-vous utilisé d'autres technos/framework pour faciliter les communications "temps réel" entre le serveur et le browser?

EntityFramework.Patterns : Repository & Unit Of Work

Publié par Fabrice Michellonet sous le(s) label(s) , le 13 juillet 2011

Je vous propose un très rapide post pour faciliter la prise en main des Patterns Repository et Unit Of Work disponibles dans EntityFramework.Patterns

Pour simplifier le sujet, le but du pattern Repository est de donner au développeur une passerelle de requêtage unifiée, via des opérations (le plus souvent) atomiques. Unit Of Work, est la quant à lui pour valider transactionnellement un ensemble de modifications faites sur les données.

Pour la version longue vous pourrez trouver votre bonheur sur le site de Martin Fowler ici pour le Repository et ici pour Unit Of Work

Partons d'un modèle simplissime comme celui ci-dessous :


public class Context : DbContext
{
    public virtual DbSet<Product> Products { get; set; }
    public virtual DbSet<ProductCategory> Categories { get; set; }

    public Context()
    {
        Database.SetInitializer(new DropCreateDatabaseAlways<Context>());
    }
}

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int? ProductCategoryId { get; set; }
    public virtual ProductCategory ProductCategory { get; set; }
}

public class ProductCategory
{
    public int Id { get; set; }
    public string Name { get; set; }
}

Commençons par créer le Repository

using(Context ctx = new Context())
{
    DbContextAdapter adapter = new DbContextAdapter(ctx);
    IRepository<Product> productRepo = new Repository<Product>(adp);
    ...
}

Interrogeons la base pour ramener l'ensemble des produits :

IEnumerable<Product> lst = productRepo.GetAll();

Ramenons le premier élément dont le nom commence par "Bike"

Product prod = productRepo.First(p => p.Name.StartsWith("Bike"));

L'utilisation de la méthode Single a pour effet de ramener un seul élément de la base de données; mais lève une exception si plusieurs éléments répondent aux critères demandés.

 productRepo.Single(p => p.Name.StartsWith("Roc"))

Find(), permet de filtrer en fonction de plusieurs critères :

IEnumerable<Product> lst = productRepo.Find(
                p => p.Id < 100 && p.Name.Contains("o") && p.Name.Length < 20);

Toutes ces méthodes acceptent optionnellement un ensemble de liens de navigation inter entités

params Expression<Func<T, object>>[] includeProperties
définissant les jointures à effectuer lors du requêtage. Ainsi :
productRepo.First(p => p.ProductCategoryId != null , p => p.ProductCategory);
charge le produit et sa catégorie associée en une seule requete SQL; Cette façon de faire nous protège du fameux problème du Select N+1 lié au lazy loading utilisé par défaut avec Entity Framework.

Après l’interrogation de la base passons a la persistance de nos données. Pour cela nous allons faire appel au pattern UnitOfWork. L'insertion d'un nouvel élément prend cette forme :

using(...)
{
   ...
   IUnitOfWork unitOfWork = new UnitOfWork(adp);
   Product p = new Product{Name = "Skateboard"};
   productRepo.Insert(p);
   unitOfWork.Commit();
}

Vous l'aurez compris la mise à jour et la suppression sont aussi simple que ca :

Product p = productRepo.First(c => c.Name == "Bike");
p.Name = "New bike";
productRepo.Update(p);

Product delete = productRepo.First(p => p.Name.StartsWith("To be")); 
productRepo.Delete(delete);

unitOfWork.Commit();

Dans les prochains jours je vous montrerais comment injecter Repository et Unit of work dans une couche Service par exemple avec un conteneur DI comme NInject.