在多个属性上构建一个动态的where子句
本文关键字:动态 一个 where 子句 属性 构建 | 更新日期: 2023-09-27 18:18:37
我有一个包含属性名称列表的List<string> IndexFields
。
我的问题是,我需要基于列表中的元素构建一个where子句。
到目前为止,我有;
var sitem = List1.Where(p => (p.GetType().GetProperty(IndexFields[0])
.GetValue(p, null) as string) == "red").FirstOrDefault();
但是这只允许我指定一个属性。我需要的是一个构建器,可以根据List<string> IndexFields
列表中的所有名称构建。
在运行时创建动态查询的最灵活的方法是使用Expression API:
例如: -
var type = typeof(T);
var properties = IndexFields.Select(x => type.GetProperty(x));
// x
var paramter = Expression.Parameter(type);
// x.Foo, x.Bar, ...
var leftHandSides = properties.Select(
x => Expression.Property(parameter, x));
// "Baz"
var rightHandSide = Expression.Constant(...);
// x.Foo == "Baz", x.Bar = "Baz", ...
var equalityExpressions = leftHandSides.Select(
x => Expression.Equal(x, rightHandSide));
// x.Foo == "Baz" && x.Bar == "Baz" && ...
var aggregatedExpressions = equalityExpressions.Aggregate(
(x, y) => Expression.AndAlso(x, y));
// x => x.Foo == "Baz" && x.Bar == "Baz" && ...
var lambda = Expression.Lambda<Func<T,bool>>(
aggregatedExpressions, parameter)
var item = List1.Where(lambda).FirstOrDefault();
像这样构建查询的一个巨大优势是,生成的表达式仍然可以被翻译成SQL用于实体框架,而在lambda的主体内使用反射是非常有限的。
尽管如此,我还是建议在使用表达式框架之前花一些时间来真正理解它。如果你能明白发生了什么,从长远来看,它会为你节省大量的时间。你可以阅读更多,例如:-
- http://www.digitallycreated.net/Blog/37/dynamic-queries-in-entity-framework-using-expression-trees
- https://stackoverflow.com/questions/1217539/net-expression-trees-tutorial
如果你正在寻找一些更快和更脏的东西,但是,你可以继续并将这些Where
子句链接到foreach
中:-
IEnumerable<T> query = List1;
foreach (var property in IndexFields)
{
// The variable "property" gets hoisted out of local context
// which messes you up if the query is being evaluated with
// delayed execution.
// If you're working in C# 6 though, you don't need to do this.
var localProperty = property;
query = query.Where(
p => (p.GetType().GetProperty(localProperty)
.GetValue(p, null) as string) == "red");
}
var sitem = query.FirstOrDefault();
您可以使用PredicateBuilder:
var predicate = PredicateBuilder.New<string>();
if (aCondition)
{
predicate = predicate.And(s => s == "this");
}
if (bCondition)
{
predicate = predicate.And(s => s == "that");
}
你可以尝试这样的东西,为我在linqpad
void Main() {
var listFields = new string[] { "Field1", "Field2" };
var listValues = new string[] { "value1", "value2" };
// prepare & show dummy data
var listItems = Enumerable.Range(1, 100).Select(aaIndex => new MyItem {
Name = string.Format("item{0}", aaIndex),
Field1 = string.Format("value{0}", aaIndex % 3),
Field2 = string.Format("value{0}", aaIndex % 7)
});
listItems.Dump();
// apply filtering
var filtered = listItems.Where(aaItem => Enumerable.Range(0, listFields.Length).All(aaIndex => {
var value1 = aaItem.GetType().GetProperty(listFields[aaIndex]).GetValue(aaItem, null);
var value2 = listValues[aaIndex];
if (value1 is IComparable) {
return ((IComparable)value1).CompareTo(value2) == 0;
}
return Convert.ToString(value1) == Convert.ToString(value2);
}));
filtered.Dump();
}
// Define other methods and classes here
class MyItem {
public string Name { get; set; }
public string Field1 { get; set; }
public string Field2 { get; set; }
}
动态linq库可以很好地处理如下内容:
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx它添加了重载来将不同的子句作为字符串,例如:
var query = List1.Where("Color1=""Red"" or Color2=""Red""");
在您的情况下,您可以从索引字段构建字符串(可能在循环中,但这里简化了)
var query = List1.Where(IndexFields[0] + "=""Red"" or " IndexFields[1] + "=Red");
要使用,请下载样例包,然后抓取LinqSamples'DynamicQuery'DynamicQuery'Dynamic.cs并与您的项目一起编译。