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?