2012-10-12 3 views
0

J'ai une classe représente un objet de filtre génériqueManipulation des références nullables lors de la construction LambdaExpression dynamiquement

public class Filter 
{ 
    public string column { get; set; } 
    public string operator { get; set; } 
    public string data { get; set; } 
} 

que je suis capable de transformer en un LambdaExpression grâce au code comme celui-ci

public LambdaExpression ToExpression(IQueryable query) { 
    LambdaExpression toReturn = null; 
    ParameterExpression parameter = Expression.Parameter(query.ElementType, "p"); 
    MemberExpression memberAccess = GetMemberExpression(column, parameter); 
    ConstantExpression filter = Expression.Constant(Convert.ChangeType(data, memberAccess.Type)); 
    WhereOperation condition = (WhereOperation)StringEnum.Parse(typeof(WhereOperation), operator); 
    LambdaExpression lambda = BuildLambdaExpression(memberAccess, filter, parameter, condition, data); 
    if (toReturn == null) { 
     toReturn = lambda; 
    } 
    return toReturn; 
} 

Conformément à la Le membre Filtercolumn peut contenir une sorte de sintax de navigation pour faire des requêtes qui est exprimée comme FieldA.FieldB.Description qui se traduit par:

  • Obtenir la valeur de type T retourné de la propriété FieldA
  • Obtenir la valeur de type T1 renvoyée par le FieldB de T
  • Obtenir la valeur de type T2 sont retournés à partir de la description de T2

le résultat est un lambda comme celui-ci: p.FieldA.FieldB.Description == "data" et je peut utiliser ce résultat en tant que paramètre de la méthode d'extension Where quelle IQueryable

Le problème se produit lorsque l'un des membres de la requête de navigation est de type Nullable. Dans ce cas, en supposant FieldA est annulable, le lambda correcte doit être

p.FieldA != null && p.FieldA.FieldB.Description == "data"

J'ai essayé de mettre en oeuvre cette vérification lors de la construction de l'objet MemberExpression en utilisant le code comme celui-ci

MemberExpression memberAccess = null; 
foreach (var property in column.Split('.')) { 
    memberAccess = MemberExpression.Property(memberAccess ?? (p as Expression), property); 
    Type memberType = memberAccess.Type; 
    if (memberType.IsGenericType && 
     memberType.GetGenericTypeDefinition() == typeof(Nullable<>)) { 

     //Create here an expression of type : memberAccess != null 
    } 
} 

qui est bon pour les types primitifs mais ne fonctionne pas, par exemple, avec d'autres références d'objet, comme les instances d'objet EF EntityReference. Je sais que je pourrais simplement ajouter une autre condition à la précédente if comme

if ((memberType.IsGenericType && 
     memberType.GetGenericTypeDefinition() == typeof(Nullable<>)) || 
     memberType.IsClass) { 
} 

mais qui me semble trop générique produire un lambda avec trop de condition et la plupart d'entre eux unuseful peut-être.

Y a-t-il un moyen de mieux identifier les références nullables?

+0

Si l'expression est destinée à Entity Framework, je ne voudrais pas déranger. 'p.FieldA.FieldB.Description ==" data "' sera simplement faux sans aucune exception si 'FieldA' ou' FieldB' est nul. Si c'est destiné à être compilé et exécuté en mémoire, c'est une autre histoire. –

+0

J'utilise la première version d'EF, celle qui fonctionne avec .NET 3.5. Si je fais une requête comme dans l'exemple, la requête ne s'applique tout simplement pas et le résultat est comme si je n'avais spécifié aucun filtre ... – Lorenzo

Répondre

3

Vous pouvez rendre le contrôle null inconditionnel. La vérification d'un type de valeur pour null n'est pas illégale.

+0

Ma classe Filter est déjà utilisée dans un contexte plus large avec des groupes et d'autres listes de règles. Si je fais le contrôle inconditionnel, le nombre d'expressions dans certains cas peut rapidement augmenter même si elles ne sont pas strictement nécessaires. Est-ce que cela affecterait les performances? – Lorenzo

+1

@Lorenzo La vérification de null n'est pas plus ou moins de travail que de vérifier si une valeur est nullable, donc la vérification pour voir si vous devez faire une vérification nulle est le même travail dans le cas d'un objet non nullable. * travailler dans le cas d'un objet nullable. – Servy

+0

@Lorenzo: Eh bien, chaque opération supplémentaire ajoute un peu à l'overhead, bien sûr. Mais d'un autre côté, la grande majorité des types utilisés dans votre scénario seront des types de référence et ont besoin de la vérification null de toute façon. –

Questions connexes