IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C# Discussion :

Besoin d'aide sur Expression.Field (Linq.Expression)


Sujet :

C#

  1. #1
    Expert confirmé
    Avatar de popo
    Homme Profil pro
    Analyste programmeur Delphi / C#
    Inscrit en
    Mars 2005
    Messages
    2 972
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Analyste programmeur Delphi / C#
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2005
    Messages : 2 972
    Par défaut Besoin d'aide sur Expression.Field (Linq.Expression)
    Bonjour,

    J'essaie d'adapter à mes besoins le projet disponible à l'adresse ci-dessous (en particulier le GenericPopulator).
    Cet objet permet de remplir automatiquement un IEnumerable<T> à partir d'un SqlDataReader.
    http://www.codeproject.com/Articles/...-from-SqlDataR

    Mon but est de placer le code du GenericPopulator dans une méthode d'extension au SqlDataReader.
    Sauf qu'au lieu de mapper une colonne du SqlDataReader à une propriété d'Objet, je souhaite la mapper au texte d'un attribut placé sur la propriété.

    Le code produit compile mais plante à l'exécution sur la ligne suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    MemberExpression DBNullMemberExpression = Expression.Field(DBNullParameterExpression, DBNullField);
    Je ne suis vraiment pas à l'aise avec les expressions et j'espère que vous pourrez m'aider

    Voici le code complet de cette méthode d'extension :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    namespace XYZ.Extensions
    {
        public static class SqlDataReaderExtension
        {
            public static IEnumerable<T> Populate<T>(this SqlDataReader reader)
            {
                List<T> result = new List<T>();
     
                if (reader != null)
                {
                    Func<SqlDataReader, T> internalReader = SqlDataReaderExtension.GetReader<T>(reader);
     
                    while (reader.Read())
                    {
                        result.Add(internalReader(reader));
                    }
                }
     
                return result;
            }
     
            static Func<SqlDataReader, T> GetReader<T>(SqlDataReader reader)
            {
                Delegate result = null;
     
                Type genericType = typeof(T);
                Type DBNullType = typeof(DBNull);
                Type readerType = typeof(SqlDataReader);
                Type populateAttributeType = typeof(PopulateAttribute);
     
                List<MemberBinding> bindings = new List<MemberBinding>();
     
                // Liste des colonnes
                List<String> columns = new List<String>();
                for (Int32 i = 0; i < reader.FieldCount; i++)
                {
                    String column = reader.GetName(i);
                    columns.Add(column);
                }
     
                // Récupération des informations sur la méthode GetValue 
                MethodInfo readerGetValueMethodInfo = readerType.GetMethod("GetValue");
                ParameterExpression readerParameterExpression = Expression.Parameter(readerType, "reader");
                ParameterExpression[] readerParametersExpression = new ParameterExpression[] { readerParameterExpression };
     
                // Récupération des informations sur le type DBNull
                FieldInfo DBNullField = DBNullType.GetField("Value");
                ParameterExpression DBNullParameterExpression = Expression.Parameter(DBNullType);
                MemberExpression DBNullMemberExpression = Expression.Field(DBNullParameterExpression, DBNullField);
     
                // Utile pour gérer le cas où deux attributs correspondent à la colonne
                List<String> foundColumns = new List<String>();
     
                foreach (PropertyInfo property in genericType.GetProperties())
                {
                    Object defaultValue;
                    String field = String.Empty;
                    Type propertyType = property.PropertyType;
     
                    // Récupération des noms de champs physiques placés dans les attributs et comparaison avec les colonnes
                    PopulateAttribute[] attributes = (Attribute.GetCustomAttributes(property, populateAttributeType) as PopulateAttribute[]);
                    for (Int32 i = 0; i < attributes.Count(); i++)
                    {
                        String attribute = attributes[i].Field;
                        if ((columns.Contains(attribute)) && (!foundColumns.Contains(attribute)))
                        {
                            foundColumns.Add(attribute);
                            field = attribute;
                            break;
                        }
                    }
     
                    // Le but est de construire à la volée une fonction de la forme suivante : 
                    // Property = IIF((reader.GetValue(columnIndex) != DBNull.Value), Convert(reader.GetValue(columnIndex)), Convert(defaultValue)
                    if (!String.IsNullOrWhiteSpace(field))
                    {
                        // Détermine la valeur par défaut selon le type
                        if (propertyType.IsValueType) defaultValue = Activator.CreateInstance(propertyType);
                        else if (propertyType.Name.Equals("String", StringComparison.OrdinalIgnoreCase)) defaultValue = String.Empty;
                        else defaultValue = null;
     
                        // Construit l'expression qui va servir à générer le code de lecture d'une données
                        ConstantExpression columnIndexConstantExpression = Expression.Constant(reader.GetOrdinal(field));
                        IEnumerable<Expression> columnsIndexesExpressions = new List<Expression> { columnIndexConstantExpression };
                        MethodCallExpression readerGetValueMethodCallExpression = Expression.Call(readerParameterExpression, readerGetValueMethodInfo, columnsIndexesExpressions);
     
     
                        // Construit les expressions qui va servir à générer le code pour vérifier que la données "IS NOT NULL"
                        ConstantExpression defaultValueConstantExpression = Expression.Constant(defaultValue);
                        BinaryExpression testExpression = Expression.NotEqual(DBNullMemberExpression, readerGetValueMethodCallExpression);
                        UnaryExpression ifTrueExpression = Expression.Convert(readerGetValueMethodCallExpression, propertyType);
                        UnaryExpression ifFalseExpression = Expression.Convert(defaultValueConstantExpression, propertyType);
                        ConditionalExpression valueConditionlExpression = Expression.Condition(testExpression, ifTrueExpression, ifFalseExpression);
     
                        // Construit l'expression qui va servir à générer le code de mapping entre la valeur et propriété
                        MemberInfo genericMemberInfo = genericType.GetMember(property.Name)[0];
                        MemberBinding genricMemberBinding = Expression.Bind(genericMemberInfo, valueConditionlExpression);
                        bindings.Add(genricMemberBinding);
                    }
                }
     
                // Construit l'expression qui va servir à générer le code de création de l'objet générique
                // et l'associer à la liste d'expression créée juste avant.
                // Cela donnera une expression de la forme :
                // new T() { FirstProperty = IFF(...), SecondProperty = IFF(...), NthProperty = IFF(...) }
                NewExpression genricNewExpression = Expression.New(genericType);
                MemberInitExpression genricInitExpression = Expression.MemberInit(genricNewExpression, bindings);
     
                // Génère l'expression lambda de la forme suivante :
                // reader => new T() { ... }
                LambdaExpression lambdaExpression = Expression.Lambda<Func<SqlDataReader, T>>(genricInitExpression, readerParametersExpression);
                result = lambdaExpression.Compile();
     
                return (result as Func<SqlDataReader, T>);
            }
        }
    }
    L'exception déclenchée est la suivante :
    {System.ArgumentException: Static field requires null instance, non-static field requires non-null instance.
    Parameter name: expression
    at System.Linq.Expressions.Expression.Field(Expression expression, FieldInfo field)
    at XYZ.Extensions.SqlDataReaderExtension.GetReader[T](SqlDataReader reader) in c:\Users\popo\Documents\Visual Studio 2013\Projects\XYZ\Extensions\Extension.cs:line 57
    at XYZ.Extensions.Mechanics.SqlDataReaderExtension.Populate[T](SqlDataReader reader) in c:\Users\popo\Documents\Visual Studio 2013\Projects\XYZ\Extensions\Extension.cs:line 19
    at XYZ.FormTest.button1_Click(Object sender, EventArgs e) in c:\Users\popo\Documents\Visual Studio 2013\Projects\XYZ\FormTest.cs:line 47
    at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
    at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
    at System.Windows.Forms.Control.WndProc(Message& m)
    at System.Windows.Forms.ButtonBase.WndProc(Message& m)
    at System.Windows.Forms.Button.WndProc(Message& m)
    at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
    at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
    at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
    at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
    at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
    at XYZ.Program.Main() in c:\Users\popo\Documents\Visual Studio 2013\Projects\XYZ\Program.cs:line 18
    at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
    at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
    at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
    at System.Threading.ThreadHelper.ThreadStart()}

  2. #2
    Expert confirmé
    Avatar de popo
    Homme Profil pro
    Analyste programmeur Delphi / C#
    Inscrit en
    Mars 2005
    Messages
    2 972
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Analyste programmeur Delphi / C#
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2005
    Messages : 2 972
    Par défaut
    Bonjour à nouveau

    Je viens de m'apercevoir d'un fait étrange.
    Si cible le Framework 3.5, cela fonctionne avec très peu de changements
    - Changer String.IsNullOrWhiteSpace par String.IsNullOrEmpty
    - Rajouter un paramètre name à Expression.Field (ce qui correspond à la deuxième signature de cette méthode de le 4.0)

    Par acquis de conscience, j'ai réessayé avec le Framework 4.0 et ses changements, ça plante au même endroit avec exactement la même exception.
    Tous nos projets C# sont écrit avec le Framework 4.0.

    Quelqu'un sait quelque de cette différence de comportement entre le 3.5 et le 4.0 ?

  3. #3
    Expert confirmé
    Avatar de popo
    Homme Profil pro
    Analyste programmeur Delphi / C#
    Inscrit en
    Mars 2005
    Messages
    2 972
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Analyste programmeur Delphi / C#
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2005
    Messages : 2 972
    Par défaut
    Y'a des jours aujourd'hui où j'éprouve une certaine antipathie envers Microsoft de changer le comportement d'une méthode d'une version de Framework à une autre.

    Après avoir farfouillé sur MSDN j'ai repéré le texte suivant :
    Citation Envoyé par Framework 4.0
    expressionType : System.Linq.Expressions.Expression
    Expression auquel la propriété Expression doit être égale. Pour static (Shared en Visual Basic), expression doit être null.
    Sur le Framework 3.5, il n'y a pas la notion de static dans le paramètre "expression"
    Citation Envoyé par Framework 3.5
    expressionType : System.Linq.Expressions.Expression

    Expression auquel la propriété Expression doit être égale.
    En mettant null, ça semble fonctionner.
    Je passe donc en résolu.

    Par contre, j'avoue n'avoir pas très bien compris la phrase
    Pour static (Shared en Visual Basic), expression doit être null.
    La méthode Field est statique dans le 3.5 comme dans la 4.0 alors à quoi correspond ce "pour static" ?
    Est-ce que c'est parce que je l'invoque à partir d'une méthode statique ?

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [MySQL] Besoin d'aide en php pour les expressions régulière
    Par totoAussi dans le forum PHP & Base de données
    Réponses: 5
    Dernier message: 14/01/2011, 21h16
  2. Besoin d'aide sur expression régulière
    Par tafkap dans le forum ActionScript 3
    Réponses: 1
    Dernier message: 02/02/2010, 16h59
  3. Debutant besoin d'aide sous visual cpp 2005 express
    Par Jhulk dans le forum Débuter
    Réponses: 2
    Dernier message: 19/10/2007, 23h10
  4. besoin d'aide sur les expressions regulieres
    Par gecko753 dans le forum C++Builder
    Réponses: 4
    Dernier message: 22/05/2007, 11h31
  5. [SimpleXML] besoin d'aide sur une expression xpath
    Par ANISSS dans le forum Bibliothèques et frameworks
    Réponses: 8
    Dernier message: 11/05/2007, 10h23

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo