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

时间:2013-06-14 10:57:45

标签: c# linq lambda

我所拥有的是List<string> IndexFields,其中包含属性名称列表。

我的问题是我需要根据列表中的元素构建一个where子句。

到目前为止,我有;

var sitem = List1.Where(p => (p.GetType().GetProperty(IndexFields[0])
  .GetValue(p, null) as string) == "red").FirstOrDefault();

但这只允许我指定一个属性。我需要的是一个可以基于List<string> IndexFields列表中的所有名称构建的构建器。

4 个答案:

答案 0 :(得分:4)

在运行时创建动态查询的最灵活方法是使用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以与Entity Framework一起使用,在lambda体内使用反射的时候真的是有限制的。

我确实建议在使用之前花一些时间来真正理解表达式框架。如果您可以了解正在发生的事情,从长远来看,它可以为您节省大量时间。

您可以阅读更多内容,例如: -


如果您正在寻找更快更脏的东西,那么您可以继续将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();

答案 1 :(得分:1)

您可以使用PredicateBuilder:

var predicate = PredicateBuilder.New<string>();
 if (aCondition)
 {
  predicate = predicate.And(s => s == "this");
 }

 if (bCondition)
 {
  predicate = predicate.And(s => s == "that");
 }

答案 2 :(得分:0)

你可以尝试这样的事情,在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; }
}

答案 3 :(得分:0)

动态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");

使用download the sample package,然后抓取LinqSamples \ DynamicQuery \ DynamicQuery \ Dynamic.cs并使用您的项目进行编译。