免责声明:我对此还比较新手,可能有更好的方法。非常感谢任何反馈。
请注意,此方法没有错误/空值检查。由于它使用反射,如果您计划在生产中使用它,应该对其进行性能分析。
public static IEnumerable<T> Filter<T>(IEnumerable<T> collection, Dictionary<string, object> filters) {
var type = typeof (T);
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
var queryable = collection.AsQueryable();
var instance = Expression.Parameter(type, "instance");
var expressions = new Stack<Expression>();
foreach (var filter in filters) {
var propertyName = filter.Key;
var property = properties.FirstOrDefault(x => x.Name == propertyName);
if (property == null)
continue;
var left = Expression.Property(instance, property);
var right = Expression.Constant(filter.Value, property.PropertyType);
var expr = Expression.Equal(left, right);
expressions.Push(expr);
}
Expression call = null;
Expression previousExpression = null;
while(expressions.Count > 0) {
var expr = expressions.Pop();
if(previousExpression == null) {
previousExpression = expr;
call = expr;
} else {
var and = Expression.AndAlso(previousExpression, expr);
call = and;
previousExpression = and;
}
}
var whereCallExpression = Expression.Call(
typeof(Queryable),
"Where",
new[] { queryable.ElementType },
queryable.Expression,
Expression.Lambda<Func<T, bool>>(call, new[] { instance }));
return queryable.Provider.CreateQuery<T>(whereCallExpression);
}
它超越了所有的过滤器并尝试找到匹配的属性。如果它找到一个属性,它会创建一个EqualExpression来比较实际值和您想要过滤的值。然后它创建一个MethodCallExpression
传递给查询提供程序。
这些表达式然后被组合起来。我认为堆栈部分是错误的,并且有更好的方法来解决它。
用法:
var persons = new List<Person> {new Person {Name = "Alex", Age = 22}, new Person {Name = "Jesper", Age = 30}}
var filters = new Dictionary<string, object>()
filters.Add("Name", "Alexander Nyquist")
var results = Filter(persons, filters)
由于它是构建表达式,因此它可以与 Linq 2 Sql(已测试)和可能的 Entity Framework 一起使用。 Linq 2 sql 生成以下查询:
SELECT [t0].[Id], [t0].[Name], [t0].[Email]
FROM [dbo].[Persons] AS [t0]
WHERE [t0].[Name] = @p0
希望这能有所帮助。