Comment créer un arbre d'expression Linq pouvant être comparé à un objet générique?
-
04-07-2019 - |
Question
J'ai un objet IQueryable et un objet de type T.
Je souhaite effectuer IQueryable (). Where (o = > o.GetProperty (nomChamp)) == objectOfTypeT.GetProperty (nomChamp))
alors ...
public IQueryable<T> DoWork<T>(string fieldName)
where T : EntityObject
{
...
T objectOfTypeT = ...;
....
return SomeIQueryable<T>().Where(o => o.GetProperty(fieldName) == objectOfTypeT.GetProperty(fieldName));
}
Fyi, GetProperty n'est pas une fonction valide. J'ai besoin de quelque chose qui remplisse cette fonction.
Suis-je en train de faire fondre le cerveau vendredi après-midi ou est-ce une chose complexe à faire?
objectOfTypeT Je peux faire ce qui suit ...
var matchToValue = Expression.Lambda(ParameterExpression
.Property(ParameterExpression.Constant(item), "CustomerKey"))
.Compile().DynamicInvoke();
Ce qui fonctionne parfaitement, il ne me manque plus que la deuxième partie:
renvoyer SomeIQueryable (). Where (o = > o.GetProperty (nomChamp) == matchValue);
La solution
Comme si:
var param = Expression.Parameter(typeof(T), "o");
var fixedItem = Expression.Constant(objectOfTypeT, typeof(T));
var body = Expression.Equal(
Expression.PropertyOrField(param, fieldName),
Expression.PropertyOrField(fixedItem, fieldName));
var lambda = Expression.Lambda<Func<T,bool>>(body,param);
return source.Where(lambda);
J'ai lancé un blog qui traitera un certain nombre de sujets relatifs aux expressions, ici .
Si vous rencontrez des problèmes, une autre option consiste à extraire d'abord la valeur de objectOfTypeT
(en utilisant la réflexion), puis à utiliser cette valeur dans Expression.Constant
. soupçonnez que tout ira bien "tel quel".
Autres conseils
D'après ce que je peux voir jusqu'à présent, il faudra que ce soit quelque chose comme ...
IQueryable<T>().Where(t =>
MemberExpression.Property(MemberExpression.Constant(t), fieldName) ==
ParameterExpression.Property(ParameterExpression.Constant(item), fieldName));
Bien que je puisse obtenir ceci pour le compiler, il ne s'exécute pas tout à fait comme il est requis.
Qu'en est-il de:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
public Func<T, TRes> GetPropertyFunc<T, TRes>(string propertyName)
{
// get the propertyinfo of that property.
PropertyInfo propInfo = typeof(T).GetProperty(propertyName);
// reference the propertyinfo to get the value directly.
return (obj) => { return (TRes)propInfo.GetValue(obj, null); };
}
public void Run()
{
List<Person> personList = new List<Person>();
// fill with some data
personList.Add(new Person { Name = "John", Age = 45 });
personList.Add(new Person { Name = "Michael", Age = 31 });
personList.Add(new Person { Name = "Rose", Age = 63 });
// create a lookup functions (should be executed ones)
Func<Person, string> GetNameValue = GetPropertyFunc<Person, string>("Name");
Func<Person, int> GetAgeValue = GetPropertyFunc<Person, int>("Age");
// filter the list on name
IEnumerable<Person> filteredOnName = personList.Where(item => GetNameValue(item) == "Michael");
// filter the list on age > 35
IEnumerable<Person> filteredOnAge = personList.Where(item => GetAgeValue(item) > 35);
}
C'est un moyen d'obtenir les valeurs d'une propriété par chaîne sans utiliser de requêtes dynamiques. L'inconvénient est que toutes les valeurs seront boxed / unboxed.