Gerar expressão <> para filtrar por uma propriedade arbitrária
-
05-07-2019 - |
Pergunta
Eu quero escrever controles de filtragem que tomem tipo de objeto T
e nome e retorno da propriedade Expression<Func<T, bool>>
que verifica o valor da propriedade passada. Não quero usar a reflexão porque receio que essas expressões não possam ser usadas pelo EF. Não posso usar delegados porque C# não possui delegados para propriedades. O que posso fazer? Talvez eu deva usar uma abordagem diferente para escrever esses controles?
Aqui está minha primeira abordagem usando a reflexão:
public string FilteringField { get; set; }
public Expression<Func<T, bool>> GetFilterExpression()
{
if (cmbValue.SelectedIndex == 1)
return (o => (bool)typeof(T).GetProperty(FilteringField).GetValue(o, null));
if (cmbValue.SelectedIndex == 2)
return (o => !(bool)typeof(T).GetProperty(FilteringField).GetValue(o, null));
return null;
}
Solução
A reflexão não é um problema aqui; A EF nem será capaz de notar a diferença. A abordagem delegada é não iniciante, a propósito (desde que você menciona EF); Em última análise, é algo como:
public static IQueryable<T> Where<T>(this IQueryable<T> query,
string propertyName, object value)
{
PropertyInfo prop = typeof(T).GetProperty(propertyName);
var param = Expression.Parameter(typeof(T), "x");
var body = Expression.Equal(
Expression.Property(param, prop),
Expression.Constant(value, prop.PropertyType)
);
var predicate = Expression.Lambda<Func<T, bool>>(body, param);
return query.Where(predicate);
}
Observe que você pode facilitar o Expression.PropertyOrField(propertyName)
; A razão pela qual não usei isso aqui é que é muito útil conhecer o tipo de membro (prop.PropertyType
) ao criar a constante - caso contrário, você poderá obter problemas com nulos.
Outras dicas
Eu sei disso uma resposta antiga, mas se alguém vê isso, eu construí este projeto:
https://github.com/poweredsoft/dynamiclinq
Que deve ser download no NuGet também:
https://www.nuget.org/packages/poweredsoft.dynamiclinq
E você pode simplesmente fazer
query.Where("FirstName", ConditionOperators.Equal, "David");