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?