在多个属性上构建一个动态的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列表中的所有名称构建。

在多个属性上构建一个动态的where子句

在运行时创建动态查询的最灵活的方法是使用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并与您的项目一起编译。