Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

Matthieu MEZIL

I love .Net

Abonnements

Actualités

Locations of visitors to this page
Des providers tiers pour EF Beta 3

Comme annoncé par David Sceppa, plusieurs providers tiers fonctionnant avec EF Beta 3 sont disponibles dès à présent (Oracle, MySQL, PostgreSQL, SQLite).

Posté le mercredi 7 mai 2008 08:33 par Matthieu MEZIL | 0 commentaire(s)

Classé sous : , ,

CompiledQuery et DataContext.LoadOptions

Quand on utilise une CompiledQuery en LINQ To SQL, on ne peut pas passer des context avec différents LoadOptions.

Si on le fait, on a une exception de type NotSupportedException:

"Compiled queries across DataContexts with different LoadOptions not supported."

Rien de surprenant à cela. En effet, quand on utilise une compiled query, l'expression tree est calculé une fois et ne sera plus recalculé par la suite. Par conséquent, on ne pourrait pas prendre en compte les éventuelles modifications sur le LoadOptions du context (passé en paramètre au délégué généré par la CompiledQuery).

Par contre ce que je n'aime pas du tout c'est que la comparaison du LoadContext se fait avec un == au lieu d'un Equals bien plus adapté !

Dans le forum msdn, j'ai pu constaté que je n'étais pas le seul à avoir eu le problème.

Par conséquent, dans le cas où vous voudriez utiliser des CompiledQuery avec des context ayant un LoadOptions, il vous faudra garder l'instance vers le LoadOptions. Tongue Tied

Posté le mardi 29 avril 2008 20:16 par Matthieu MEZIL | 0 commentaire(s)

Classé sous : , ,

Parallel Framework, ce n'est pas magique mais ça peut être bien sympa à condition de bien l'utiliser

Comme plusieurs d'entre vous j'ai eu l'occasion de voir des démos assez bleuffantes sur le Parallel Framework (dont vous pouvez télécharger la CTP de décembre ici), à commencer par celle lors de la pleinière du lundi matin aux techdays.

Pour ceux qui l'ignorent ce Framework permet de très simplement paralléliser notre code afin d'utiliser l'ensemble des coeurs présents sur nos machines modernes.

J'ai voulu voir ce que ça donnait sur un algo aussi simple que le tri à bulle dont voici une implémentation "classique" :

static int[] ClassicBubbleSort(int[] array)

{

    int maxI = array.Length - 1;

    bool continueSort = true;

    while (continueSort)

    {

        continueSort = false;

        for (int i = 0; i < maxI; ) // ouais je sais là j'abuse à complexifié l'exemple pour rien mais je trouve ça plus fun comme ça Smile et c'est plus optimal

            if (Sort(ref array[i++], ref array[ i ]))

                continueSort = true;

    }

    return array;

}

static bool Sort(ref int item1, ref int item2)

{

    if (item1 > item2)

    {

        int item1Bis = item1;

        item1 = item2;

        item2 = item1Bis;

        return true;

    }

    return false;

}

Avec le parallel framework, on a une méthode For qui va nous permettre de paralléliser les itérations dans la boucle. Une seule contrainte par contre : l'état à l'itération in ne doit pas dépendre de l'état de in-1.

Aussi, il faut repenser l'algo du tri à bulle.

C'est ce que j'ai fait comme ceci dans un premier temps :

static int[] ParallelBubbleSort1(int[] array)

{

    int maxI = array.Length - 1;

    bool continueSort = true;

    while (continueSort)

    {

        continueSort = false;

        System.Threading.Parallel.For(0, maxI, 2, i =>

        {

            if (Sort(ref array[ i ], ref array[i + 1]))

                continueSort = true;

        });

        System.Threading.Parallel.For(1, maxI, 2, i =>

        {

            if (Sort(ref array[ i ], ref array[i + 1]))

                continueSort = true;

        });

    }

    return array;

}

Mais là, les temps de réponses sont en réalité bien moins bons qu'avec notre première implémentation. Pourquoi ?

La parallélisation du code, c'est pas gratuit et ça prend du temps. Et vu la simplicité du code qui est effectué dans la boucle (la méthode Sort), ça ne vaut pas le coup. De plus, on peut supposer (il faudra que je creuse), que l'affectation de continueSort se fait avec un mutex pour protéger l'écriture mais ce qui a pour effet de ralentir les threads qui vont se retrouver bloquer à cause d'accès concurrentiel.

Du coup, on pourrait être tenté de transformer notre code comme ceci en appliquant l'adage diviser pour mieux reigner :

static int[] ParallelBubbleSort2(int[] array)

{

    if (array.Length == 0)

        return array;

    int maxI = array.Length;

    bool continueSort = true;

    int nbProc = Environment.ProcessorCount;

    Action[] actions = new Action[nbProc];

    Action[] actions2 = new Action[nbProc - 1];

    bool[] continueSorts = new bool[nbProc];

    int middleI = (maxI--) / nbProc;

    int maxIndexProc = nbProc - 1;

    for (int indexProc = 0; indexProc < maxIndexProc; indexProc++)

    {

        int first = indexProc * middleI;

        int last = first + middleI;

        int indexPropCopy = indexProc;

        actions[indexProc] = () =>

            {

                continueSorts[indexPropCopy] = false;

                for (int i = first; i < last; )

                    if (Sort(ref array[i++], ref array[ i ]))

                        continueSorts[indexPropCopy] = true;

            };

        actions2[indexProc] = () =>

            {

            if (Sort(ref array[last], ref array[last + 1]))

                continueSorts[indexPropCopy] = true;

            };

    }

    actions[maxIndexProc] = () =>

        {

            continueSorts[maxIndexProc] = false;

            for (int i = maxIndexProc * middleI; i < maxI; )

                if (Sort(ref array[i++], ref array[ i ]))

                    continueSorts[maxIndexProc] = true;

        };

    while (continueSort)

    {

        System.Threading.Parallel.Do(actions);

        System.Threading.Parallel.Do(actions2);

        continueSort = continueSorts.Any(continueSortTmp => continueSortTmp);

    }

    return array;

}

Mais là aussi, mes quelques tests montrent que ce code est moins performant que la version de base.

Dans un premier temps, on va supprimer la parallélisation sur actions2 qui, étant extrèmement restreinte nous fait plus perdre en performance qu'autre chose :

static int[] ParallelBubbleSort3(int[] array)

{

    if (array.Length == 0)

        return array;

    int maxI = array.Length;

    bool continueSort = true;

    int nbProc = Environment.ProcessorCount;

    Action[] actions = new Action[nbProc];

    Action[] actions2 = new Action[nbProc - 1];

    bool[] continueSorts = new bool[nbProc];

    int middleI = (maxI--) / nbProc;

    int maxIndexProc = nbProc - 1;

    for (int indexProc = 0; indexProc < maxIndexProc; indexProc++)

    {

        int first = indexProc * middleI;

        int last = first + middleI;

        int indexPropCopy = indexProc;

        actions[indexProc] = () =>

            {

                continueSorts[indexPropCopy] = false;

                for (int i = first; i < last; )

                    if (Sort(ref array[i++], ref array[ i ]))

                        continueSorts[indexPropCopy] = true;

            };

        actions2[indexProc] = () =>

            {

            if (Sort(ref array[last], ref array[last + 1]))

                continueSorts[indexPropCopy] = true;

            };

    }

    actions[maxIndexProc] = () =>

        {

            continueSorts[maxIndexProc] = false;

            for (int i = maxIndexProc * middleI; i < maxI; )

                if (Sort(ref array[i++], ref array[ i ]))

                    continueSorts[maxIndexProc] = true;

        };

    while (continueSort)

    {

        System.Threading.Parallel.Do(actions);

        foreach (Action action2 in actions2)

            action2();

        continueSort = continueSorts.Any(continueSortTmp => continueSortTmp);

    }

    return array;

}

Afin d'éviter d'aller n fois au même index dans le tableau, j'ai essayé une approche par pointeur pour optimiser mon code :

static int[] ParallelBubbleSort4(int[] array)

{

    if (array.Length == 0)

        return array;

    int maxI = array.Length;

    int nbProc = Environment.ProcessorCount;

    Action[] actions = new Action[nbProc];

    Action[] actions2 = new Action[nbProc - 1];

    bool[] continueSorts = new bool[nbProc];

    bool continueSort = true;

    int middleI = (maxI--) / nbProc;

    int maxIndexProc = nbProc - 1;

    for (int indexProc = 0; indexProc < maxIndexProc; indexProc++)

    {

        int first = indexProc * middleI;

        int last = first + middleI;

        int indexPropCopy = indexProc;

        actions[indexProc] = () =>

        {

            unsafe

            {

                fixed (bool* continueSortTmp = &continueSorts[indexPropCopy])

                {

                    *continueSortTmp = false;

                    for (int i = first; i < last; )

                    {

                        fixed (int* item1 = &array[i++])

                        {

                            fixed (int* item2 = &array[ i ])

                            {

                                if (Sort(item1, item2))

                                    *continueSortTmp = true;

                            }

                        }

                    }

                }

            }

        };

        actions2[indexProc] = () =>

        {

            unsafe

            {

                fixed (int* item1 = &array[last])

                {

                    fixed (int* item2 = &array[last + 1])

                    {

                        if (Sort(ref array[last], ref array[last + 1]))

                            continueSorts[indexPropCopy] = true;

                    }

                }

            }

        };

    }

    actions[maxIndexProc] = () =>

    {

        unsafe

        {

            fixed (bool* continueSortTmp = &continueSorts[maxIndexProc])

            {

                *continueSortTmp = false;

                for (int i = maxIndexProc * middleI; i < maxI; )

                    fixed (int* item1 = &array[i++])

                    {

                        fixed (int* item2 = &array[ i ])

                        {

                            if (Sort(item1, item2))

                                *continueSortTmp = true;

                        }

                    }

            }

        }

    };

    while (continueSort)

    {

        System.Threading.Parallel.Do(actions);

        foreach (Action action2 in actions2)

            action2();

        continueSort = continueSorts.Any(continueSortTmp => continueSortTmp);

    }

    return array;

}

unsafe static bool Sort(int* item1, int* item2)

{

    int item1Value = *item1;

    int item2Value = *item2;

    if (item1Value > item2Value)

    {

        *item1 = item2Value;

        *item2 = item1Value;

        return true;

    }

    return false;

}

Ce qu'il faut noter c'est que la version parallélisable est de mieux en mieux mais reste tout de même moins performante que la version "classique" dans le cadre de mes tests. Par contre, elles monopolisent bien 100% de mes deux coeurs.

J'ai alors pensé à (encore) un autre algo.

Mon algo classique n'est pas optimal loin de là. Je l'ai donc repensé comme ceci :

static int[] ClassicBubbleSort2(int[] array)

{

    int maxI = array.Length - 1;

    for (int i = 0; i < maxI; i ++)

    {

        if (!Sort(ref array[ i ], ref array[i + 1]))

            continue;

        for (int j = i; j > 0 && Sort(ref array[j - 1], ref array[j]); j--) ;

    }

    return array;

}

Cet algo n'est pas parallélisable car in dépend de in-1. La seule possibilité éventuelle, mais qui n'utiliserait pas le Parallel Framework, serait de créer un thread pour la deuxième boucle for pour continuer la première en même temps mais, dans le cas où on repasserait dans la deuxième boucle for, attendre que le thread soit terminé. Bref, bof.

Conclusion : la parallélisation du code est devenue très facile et peux s'avérer très efficace cependant, paralléliser un algo n'est pas toujours la meilleure solution et il vaut mieux parfois tout simplement optimiser notre algo écrit sans prendre en compte la problématique multi-coeur.

Il est important de noter également que les performances sont très relatives au nombre de coeurs et, dans le cas du tri à bulle, à la taille du tableau à trier.

Mon post aurait pu s'arrêter là mais j'ai persévéré et je me suis dit que j'allais utiliser ce nouvel algo et, parallélement, j'allais prémaché le travail :

static int[] ParallelBubbleSort5(int[] array)

{

    int maxI = array.Length;

    int nbProc = Environment.ProcessorCount;

    Action[] actions = new Action[nbProc];

    Action[] actions2 = new Action[nbProc - 1];

    int middleI = (maxI--) / nbProc;

    int maxIndexProc = nbProc - 1;

    for (int indexProc = 0; indexProc < maxIndexProc; indexProc++)

    {

        int first = indexProc * middleI;

        int last = first + middleI - 1;

        int indexPropCopy = indexProc;

        actions[indexProc] = () =>

        {

            unsafe

            {

                for (int i = first; i < last; )

                {

                    fixed (int* item1 = &array[i++])

                    {

                        fixed (int* item2 = &array[ i ])

                        {

                            if (!Sort(item1, item2))

                                continue;

                        }

                    }

                    for (int j = i - 1; j > first && Sort(ref array[j - 1], ref array[j]); j--) ;

                }

            }

        };

    }

    actions[maxIndexProc] = () =>

    {

        unsafe

        {

            int first = maxIndexProc * middleI;

            for (int i = first; i < maxI; )

            {

                fixed (int* item1 = &array[i++])

                {

                    fixed (int* item2 = &array[ i ])

                    {

                        if (!Sort(item1, item2))

                            continue;

                    }

                }

                for (int j = i - 1; j > first && Sort(ref array[j - 1], ref array[j]); j--) ;

            }

        }

    };

    System.Threading.Parallel.Do(actions);

    while (nbProc != 1)

    {

        maxIndexProc = nbProc - 1;

        middleI = maxI / nbProc;

        Parallel.Do(

            () =>

            {

                Parallel.For(1, maxIndexProc - 1, (indexProc) =>

                {

                    unsafe

                    {

                        int first = middleI * indexProc;

                        int last = first + middleI;

                        for (int i = first; i <= last; )

                        {

                            fixed (int* item1 = &array[i++])

                            {

                                fixed (int* item2 = &array[ i ])

                                {

                                    if (!Sort(item1, item2))

                                        continue;

                                }

                            }

                            for (int j = i - 1; j > 0 && Sort(ref array[j - 1], ref array[j]); j--) ;

                        }

                    }

                });

            },

        () =>

        {

            unsafe

            {

                for (int i = middleI * maxIndexProc; i < maxI; )

                {

                    fixed (int* item1 = &array[i++])

                    {

                        fixed (int* item2 = &array[ i ])

                        {

                            if (!Sort(item1, item2))

                                continue;

                        }

                    }

                    for (int j = i - 1; j > 0 && Sort(ref array[j - 1], ref array[j]); j--) ;

                }

            }

        });

        nbProc /= 2;

    }

    return array;

}

Voici les benchs que j'ai obtenu avec mon portable Dual Core :

Temps en ms pour un tableau de 1 000 éléments (Random) :

ClassicBubbleSort     8
ClassicBubbleSort2    2
ParallelBubbleSort1  31
ParallelBubbleSort2  41
ParallelBubbleSort3  37
ParallelBubbleSort4  22
ParallelBubbleSort5   5

Temps en ms pour un tableau de 100 000 éléments (Random) :

ClassicBubbleSort     92 444
ClassicBubbleSort2    17 675
ParallelBubbleSort1  202 874
ParallelBubbleSort2  141 495
ParallelBubbleSort3  140 160
ParallelBubbleSort4   96 079
ParallelBubbleSort5   17 418

Temps en ms pour un tableau de 500 000 éléments (Random) :

ClassicBubbleSort2   454 147
ParallelBubbleSort5  416 394

Temps en ms pour un tableau de 1 000 000 éléments (Random) :

ClassicBubbleSort2   1 801 093
ParallelBubbleSort5  1 759 334

Posté le mercredi 23 avril 2008 21:27 par Matthieu MEZIL | 3 commentaire(s)

Classé sous : , ,

Après le VS2008 Training Kit, la suite : .NET 3.5 Enhancements Training Kit

Un nouveau training kit est disponible afin de compléter le précédent.

Ce nouveau kit contient 6 labs :

  1. ADO.NET Data Services
  2. ADO.NET Entity Framework
  3. ASP.NET AJAX History
  4. ASP.NET Dynamic Data
  5. ASP.NET MVC
  6. ASP.NET Silverlight controls

Pour plus d'info, voici le post de Jonathan Carter.

Posté le lundi 21 avril 2008 07:29 par Matthieu MEZIL | 2 commentaire(s)

Classé sous : , , ,

Revenons sur l'histoire du Like avec LINQ To Entities

J'avais précédemment bloggué sur le problème que posait le where et qui imposait pour ainsi dire de faire du esql.

Cela fait maintenant un certain temps que j'ai téléchargé les extensions d'EF mais que je n'ai pas eu le temps de l'utiliser. Du coup, je profite du trajet entre le campus MS et mon hôtel pour le faire.

Et voilà que je tombe sur le code suivant :

context.CategorySet.Where("it.Name like 'B%'");

Cool ! J

Posté le jeudi 17 avril 2008 03:43 par Matthieu MEZIL | 0 commentaire(s)

Classé sous : , , ,

Un bug avec l'intelisence LINQ VB

Le group by en VB et en C# est très différent.

Avec VB, l'intellisence n'a pas intégré cela. Voici un exemple :

Alors qu'en réalité, g n'est pas un IEnumerable(Of Product) mais un decimal? !!!

D'où l'arreur de compilation suivante :

'First' is not a member of 'Decimal?'.

Posté le mercredi 16 avril 2008 02:18 par Matthieu MEZIL | 0 commentaire(s)

Classé sous : , , ,

Check-in en québécois

Apparemment ce n?est pas vrai pour tous les québécois mais certains utilisent l?expression suivante pour dire qu?ils ont fait un check-in :

« J?ai pouleté mon code »

Check-in -> Chicken -> Poulet -> Pouletter.

Quand j?entends ça, je me demande si je n?ai pas des origines québécoises. Wink

 

Posté le mardi 15 avril 2008 17:35 par Matthieu MEZIL | 9 commentaire(s)

Classé sous :

LINQ To SQL Table -> DataTable, génération de code à la volée, V3

Suite à la nouvelle remarque de Jean-Baptiste, mon code devient finalement ceci :

namespace ConsoleApplication81

{

    class Program

    {

        static void Main(string[] args)

        {

            using (var context = new DataClasses1DataContext())

            {

                var dt = LinqTableToDataTableHelper<Category>.GetDataTableFromLINQTable(context.Categories);

            }

        }

    }

    public static class LinqTableToDataTableHelper<LTT>

            where LTT : class

    {

        private static Func<Table<LTT>, DataTable> _generatedMethodDelegate;

 

        public static DataTable GetDataTableFromLINQTable(Table<LTT> linqTable)

        {

            if (_generatedMethodDelegate == null)

                _generatedMethodDelegate = GenerateTableConverter().CreateDelegate(typeof(Func<Table<LTT>, DataTable>)) as Func<Table<LTT>, DataTable>;

            return _generatedMethodDelegate(linqTable);

        }

 

        public static DynamicMethod GenerateTableConverter()

        {

            var dynamicMethod = new DynamicMethod("Convert", typeof(DataTable), new Type[] { typeof(Table<LTT>) });

            var convertIlGenerator = dynamicMethod.GetILGenerator();

            convertIlGenerator.DeclareLocal(typeof(DataTable));

            convertIlGenerator.DeclareLocal(typeof(IEnumerator<LTT>));

            convertIlGenerator.DeclareLocal(typeof(LTT));

            convertIlGenerator.DeclareLocal(typeof(DataRow));

            var propertyValue = convertIlGenerator.DeclareLocal(typeof(object));

            convertIlGenerator.Emit(OpCodes.Ldarg_0);

            var convertIlGeneratorArgOkLabel = convertIlGenerator.DefineLabel();

            convertIlGenerator.Emit(OpCodes.Brtrue_S, convertIlGeneratorArgOkLabel);

            convertIlGenerator.Emit(OpCodes.Newobj, typeof(ArgumentException).GetConstructor(new Type[0]));

            convertIlGenerator.Emit(OpCodes.Throw);

            convertIlGenerator.MarkLabel(convertIlGeneratorArgOkLabel);

 

            convertIlGenerator.Emit(OpCodes.Newobj, typeof(DataTable).GetConstructor(new Type[0]));

            convertIlGenerator.Emit(OpCodes.Stloc_0);

            var properties = typeof(LTT).GetProperties().Where(p => p.GetAttribute<ColumnAttribute>() != null);

            foreach (var pi in properties)

            {

                convertIlGenerator.Emit(OpCodes.Ldloc_0);

                convertIlGenerator.Emit(OpCodes.Callvirt, typeof(DataTable).GetMethod("get_Columns"));

                convertIlGenerator.Emit(OpCodes.Ldstr, pi.Name);

                if (pi.PropertyType.IsGenericType && pi.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))

                    convertIlGenerator.Emit(OpCodes.Ldtoken, pi.PropertyType.GetGenericArguments()[0]);

                else

                    convertIlGenerator.Emit(OpCodes.Ldtoken, pi.PropertyType);

                convertIlGenerator.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle", new Type[] { typeof(RuntimeTypeHandle) }));

                convertIlGenerator.Emit(OpCodes.Callvirt, typeof(DataColumnCollection).GetMethod("Add", new Type[] { typeof(string), typeof(Type) }));

                convertIlGenerator.Emit(OpCodes.Pop);

            }

            var convertIlGeneratorEntitiesLoopLabel = convertIlGenerator.DefineLabel();

            convertIlGenerator.Emit(OpCodes.Ldarg_0);

            convertIlGenerator.Emit(OpCodes.Callvirt, typeof(IEnumerable<LTT>).GetMethod("GetEnumerator"));

            convertIlGenerator.Emit(OpCodes.Stloc_1);

            convertIlGenerator.MarkLabel(convertIlGeneratorEntitiesLoopLabel);

            convertIlGenerator.Emit(OpCodes.Ldloc_1);

            convertIlGenerator.Emit(OpCodes.Callvirt, typeof(IEnumerator).GetMethod("MoveNext"));

            var convertIlGeneratorEntitiesEndLoopLable = convertIlGenerator.DefineLabel();

            convertIlGenerator.Emit(OpCodes.Brfalse, convertIlGeneratorEntitiesEndLoopLable);

            convertIlGenerator.Emit(OpCodes.Ldloc_1);

            convertIlGenerator.Emit(OpCodes.Callvirt, typeof(IEnumerator<LTT>).GetMethod("get_Current"));

            convertIlGenerator.Emit(OpCodes.Stloc_2);

            convertIlGenerator.Emit(OpCodes.Ldloc_0);

            convertIlGenerator.Emit(OpCodes.Callvirt, typeof(DataTable).GetMethod("get_Rows"));

            convertIlGenerator.Emit(OpCodes.Ldloc_0);

            convertIlGenerator.Emit(OpCodes.Callvirt, typeof(DataTable).GetMethod("NewRow"));

            convertIlGenerator.Emit(OpCodes.Stloc_3);

            foreach (var pi in properties)

            {

                convertIlGenerator.Emit(OpCodes.Ldloc_2);

                convertIlGenerator.Emit(OpCodes.Callvirt, typeof(LTT).GetMethod("get_" + pi.Name, new Type[0]));

                if (pi.PropertyType.IsValueType)

                    convertIlGenerator.Emit(OpCodes.Box, pi.PropertyType);

                convertIlGenerator.Emit(OpCodes.Stloc, propertyValue);

                convertIlGenerator.Emit(OpCodes.Ldloc, propertyValue);

                var convertIlGeneratorNextPropertyLabel = convertIlGenerator.DefineLabel();

                convertIlGenerator.Emit(OpCodes.Brfalse_S, convertIlGeneratorNextPropertyLabel);

                convertIlGenerator.Emit(OpCodes.Ldloc_3);

                convertIlGenerator.Emit(OpCodes.Ldstr, pi.Name);

                convertIlGenerator.Emit(OpCodes.Ldloc, propertyValue);

                convertIlGenerator.Emit(OpCodes.Callvirt, typeof(DataRow).GetMethod("set_Item", new Type[] { typeof(string), typeof(object) }));

                convertIlGenerator.MarkLabel(convertIlGeneratorNextPropertyLabel);

            }

            convertIlGenerator.Emit(OpCodes.Ldloc_3);

            convertIlGenerator.Emit(OpCodes.Callvirt, typeof(DataRowCollection).GetMethod("Add", new Type[] { typeof(DataRow) }));

            convertIlGenerator.Emit(OpCodes.Br, convertIlGeneratorEntitiesLoopLabel);

            convertIlGenerator.MarkLabel(convertIlGeneratorEntitiesEndLoopLable);

            convertIlGenerator.Emit(OpCodes.Ldloc_0);

            convertIlGenerator.Emit(OpCodes.Ret);

 

            return dynamicMethod;

        }

    }

}

namespace System.Reflection

{

    public static class PropertyInfoExtension

    {

        public static T GetAttribute<T>(this PropertyInfo pi) where T : Attribute

        {

            object[] attributes = pi.GetCustomAttributes(typeof(T), true);

            if (attributes.Length == 0)

                return null;

            return attributes[0] as T;

        }

    }

}

Posté le lundi 14 avril 2008 17:49 par Matthieu MEZIL | 0 commentaire(s)

Classé sous : , , ,

LINQ To SQL Table -> DataTable, génération de code à la volée, V2

C'est pas parce que je suis au MVP Summit que ça va m'empêcher de coder la nuit. Wink

J'avais précédemment blogué sur la génération de code à la volée qui permettait de convertir une Table LINQ To SQL en DataTable.

Suite à

  • la remarque de Jean-Baptiste
  • le fait que ça m'embêtait de garder un appel par réflection et que je voulais utiliser un expression tree à la place
  • le fait que je me suis dit que plutôt que de mettre les méthodes génériques et d'avoir un dictionnaire avec le type en clé, je ferais mieux de mettre la généricité au niveau de la classe

J'ai repris mon code pour ceci :

namespace ConsoleApplication81

{

    class Program

    {

        static void Main(string[] args)

        {

            using (var context = new DataClasses1DataContext())

            {

                var dt = LinqTableToDataTableHelper<Category>.GetDataTableFromLINQTable(context.Categories);

            }

        }

    }

    public static class LinqTableToDataTableHelper<LTT>

            where LTT : class

    {

        private static DynamicMethod _generatedType;

        private static Delegate _lambdaExprCompiled;

 

        public static DataTable GetDataTableFromLINQTable(Table<LTT> linqTable)

        {

            if (_lambdaExprCompiled == null)

            {

                var parameterExpression = Expression.Parameter(typeof(Table<LTT>), "linqTable");

                var lambdaExpression = Expression.Lambda(

                                        Expression.Call(GetGeneratedMethod(), new ParameterExpression[] { parameterExpression }),

                                        parameterExpression);

                _lambdaExprCompiled = lambdaExpression.Compile();

            }

            return _lambdaExprCompiled.DynamicInvoke(linqTable) as DataTable;

        }

 

        private static DynamicMethod GetGeneratedMethod()

        {

            if (_generatedType != null)

                return _generatedType;

            return (_generatedType = GenerateTableConverter());

        }

 

        public static DynamicMethod GenerateTableConverter()

        {

            var dynamicMethod = new DynamicMethod("Convert", typeof(DataTable), new Type[] { typeof(Table<LTT>) });

            var convertIlGenerator = dynamicMethod.GetILGenerator();

            convertIlGenerator.DeclareLocal(typeof(DataTable));

            convertIlGenerator.DeclareLocal(typeof(IEnumerator<LTT>));

            convertIlGenerator.DeclareLocal(typeof(LTT));

            convertIlGenerator.DeclareLocal(typeof(DataRow));

            var propertyValue = convertIlGenerator.DeclareLocal(typeof(object));

            convertIlGenerator.Emit(OpCodes.Ldarg_0);

            var convertIlGeneratorArgOkLabel = convertIlGenerator.DefineLabel();

            convertIlGenerator.Emit(OpCodes.Brtrue_S, convertIlGeneratorArgOkLabel);

            convertIlGenerator.Emit(OpCodes.Newobj, typeof(ArgumentException).GetConstructor(new Type[0]));

            convertIlGenerator.Emit(OpCodes.Throw);

            convertIlGenerator.MarkLabel(convertIlGeneratorArgOkLabel);

 

            convertIlGenerator.Emit(OpCodes.Newobj, typeof(DataTable).GetConstructor(new Type[0]));

            convertIlGenerator.Emit(OpCodes.Stloc_0);

            var properties = typeof(LTT).GetProperties().Where(p => p.GetAttribute<ColumnAttribute>() != null);

            foreach (var pi in properties)

            {

                convertIlGenerator.Emit(OpCodes.Ldloc_0);

                convertIlGenerator.Emit(OpCodes.Callvirt, typeof(DataTable).GetMethod("get_Columns"));

                convertIlGenerator.Emit(OpCodes.Ldstr, pi.Name);

                if (pi.PropertyType.IsGenericType && pi.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))

                    convertIlGenerator.Emit(OpCodes.Ldtoken, pi.PropertyType.GetGenericArguments()[0]);

                else

                    convertIlGenerator.Emit(OpCodes.Ldtoken, pi.PropertyType);

                convertIlGenerator.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle", new Type[] { typeof(RuntimeTypeHandle) }));

                convertIlGenerator.Emit(OpCodes.Callvirt, typeof(DataColumnCollection).GetMethod("Add", new Type[] { typeof(string), typeof(Type) }));

                convertIlGenerator.Emit(OpCodes.Pop);

            }

            var convertIlGeneratorEntitiesLoopLabel = convertIlGenerator.DefineLabel();

            convertIlGenerator.Emit(OpCodes.Ldarg_0);

            convertIlGenerator.Emit(OpCodes.Callvirt, typeof(IEnumerable<LTT>).GetMethod("GetEnumerator"));

            convertIlGenerator.Emit(OpCodes.Stloc_1);

            convertIlGenerator.MarkLabel(convertIlGeneratorEntitiesLoopLabel);

            convertIlGenerator.Emit(OpCodes.Ldloc_1);

            convertIlGenerator.Emit(OpCodes.Callvirt, typeof(IEnumerator).GetMethod("MoveNext"));

            var convertIlGeneratorEntitiesEndLoopLable = convertIlGenerator.DefineLabel();

            convertIlGenerator.Emit(OpCodes.Brfalse, convertIlGeneratorEntitiesEndLoopLable);

            convertIlGenerator.Emit(OpCodes.Ldloc_1);

            convertIlGenerator.Emit(OpCodes.Callvirt, typeof(IEnumerator<LTT>).GetMethod("get_Current"));

            convertIlGenerator.Emit(OpCodes.Stloc_2);

            convertIlGenerator.Emit(OpCodes.Ldloc_0);

            convertIlGenerator.Emit(OpCodes.Callvirt, typeof(DataTable).GetMethod("get_Rows"));

            convertIlGenerator.Emit(OpCodes.Ldloc_0);

            convertIlGenerator.Emit(OpCodes.Callvirt, typeof(DataTable).GetMethod("NewRow"));

            convertIlGenerator.Emit(OpCodes.Stloc_3);

            foreach (var pi in properties)

            {

                convertIlGenerator.Emit(OpCodes.Ldloc_2);

                convertIlGenerator.Emit(OpCodes.Callvirt, typeof(LTT).GetMethod("get_" + pi.Name, new Type[0]));

                if (pi.PropertyType.IsValueType)

                    convertIlGenerator.Emit(OpCodes.Box, pi.PropertyType);

                convertIlGenerator.Emit(OpCodes.Stloc, propertyValue);

                convertIlGenerator.Emit(OpCodes.Ldloc, propertyValue);

                var convertIlGeneratorNextPropertyLabel = convertIlGenerator.DefineLabel();

                convertIlGenerator.Emit(OpCodes.Brfalse_S, convertIlGeneratorNextPropertyLabel);

                convertIlGenerator.Emit(OpCodes.Ldloc_3);

                convertIlGenerator.Emit(OpCodes.Ldstr, pi.Name);

                convertIlGenerator.Emit(OpCodes.Ldloc, propertyValue);

                convertIlGenerator.Emit(OpCodes.Callvirt, typeof(DataRow).GetMethod("set_Item", new Type[] { typeof(string), typeof(object) }));

                convertIlGenerator.MarkLabel(convertIlGeneratorNextPropertyLabel);

            }

            convertIlGenerator.Emit(OpCodes.Ldloc_3);

            convertIlGenerator.Emit(OpCodes.Callvirt, typeof(DataRowCollection).GetMethod("Add", new Type[] { typeof(DataRow) }));

            convertIlGenerator.Emit(OpCodes.Br, convertIlGeneratorEntitiesLoopLabel);

            convertIlGenerator.MarkLabel(convertIlGeneratorEntitiesEndLoopLable);

            convertIlGenerator.Emit(OpCodes.Ldloc_0);

            convertIlGenerator.Emit(OpCodes.Ret);

 

            return dynamicMethod;

        }

    }

}

namespace System.Reflection

{

    public static class PropertyInfoExtension

    {

        public static T GetAttribute<T>(this PropertyInfo pi) where T : Attribute

        {

            object[] attributes = pi.GetCustomAttributes(typeof(T), true);

            if (attributes.Length == 0)

                return null;

            return attributes[0] as T;

        }

    }

}

Posté le lundi 14 avril 2008 09:00 par Matthieu MEZIL | 5 commentaire(s)

Classé sous : , , ,

MVP Summit 2008

En direct de Seattle, j'aimerais une fois de plus remercier ma femme et Winwise.

Après un très bon vol avec le commandant Redo (c'est même pas une blague Smile), je découvre Seattle en companie des autres MVPs WW :

Et bien sûr Daniel notre RD.

Posté le dimanche 13 avril 2008 16:50 par Matthieu MEZIL | 1 commentaire(s)

Classé sous : ,

EF v1 et Data Services Framework v1, sortie en même temps que VS 2008 SP1 & .NET 3.5 SP1
Alors que l'on devrait pouvoir prochainement profiter d'une nouvelle Beta de l'Entity Framework et du designer d'EDM, l' ADO .Net team la sortie d'EF v1 et de Data Services Framework v1 se ferait en même temps que la sortie du SP1 de VS2008 et du SP1 du framework .Net 3.5

Posté le jeudi 10 avril 2008 07:07 par Matthieu MEZIL | 3 commentaire(s)

Classé sous : , , ,

Compiled Query performance

Une requête LINQ To SQL ou LINQ To Entities est traduite en un Expression Tree qui va ensuite être optimisé puis tarduit à son tour en SQL.

Le fait d'utiliser des CompiledQueries à la place de requêtes LINQ classique fait qu'une fois que votre requête LINQ a été traduite, vous allez pouvoir conserver la traduction en Expression Tree. Ainsi à l'avenir, l'exécution de votre requête sera plus rapide.

Pourquoi ne faut-il pas utiliser systématiquement les Compiled Queries? Parce qu'il faut garder en mémoire l'Expression Tree.

Maintenant la question qui subsisse est la suivante : quelle va être le gain de performances en utilisant des Compiled Queries?

Julie répond à cette question pour LINQ To Entities ici. Dans le même temps, elle expose la syntaxe des Compiled Queries en C# et en VB .Net.

Posté le mercredi 2 avril 2008 17:00 par Matthieu MEZIL | 1 commentaire(s)

Classé sous : , , , , ,

MVP : j'ai failli oublié la photo :-)

Et oui, je l'avoue, suite à une private joke, je roule, depuis presque un an, en toute illégalité en MVPmobile. Maintenant que je suis MVP, je dois tenir mes engagements en publiant une photo.

 

 

Posté le mercredi 2 avril 2008 00:07 par Matthieu MEZIL | 6 commentaire(s)

Classé sous :

EDM designer CTP3 ou Beta 1, Update Model From DB
Ceux qui ont un peu joué avec le designer le savent, c'est encore une CTP. La mise à jour du modèle à partir de la base n'est pas franchement au point. Avec la prochaine version (CTP3 ou Beta 1), il y a beaucoup d'améliorations et notament sur cette partie. Noam Ben-Ami nous en dit plus

Posté le mardi 1 avril 2008 23:11 par Matthieu MEZIL | 0 commentaire(s)

Classé sous : , , ,

Dad yesterday, MVP today, what will happen tomorrow?

Et oui, moi aussi j'ai eu le plaisir et l'honneur de recevoir un mail m'informant que j'étais MVP. Big Smile

MVP VB... Non je déconne, poisson d'avril, MVP C# bien sûr. (MVP VB ne vous ofusquez pas, c'est une private joke)

Ce qui implique MVP Summit. A ce propos, j'aimerais remercier Winwise mais aussi tout particulièrent ma femme qui a eu l'extrème gentillesse de se faire déclencher pour accoucher plus tôt pour que je puisse y aller.

P.S. : l'idée est d'elle.

P.S. 2 : Moi je dis, une femme comme ça, il faut la garder. Vous comprenez maintenant pourquoi je suis marié. Et non ce n'est pas que pour les impôts lol.

Posté le mardi 1 avril 2008 21:08 par Matthieu MEZIL | 11 commentaire(s)

Classé sous :

Histoire de threads à moins que ce ne soi