3

我所拥有的是一个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

4 回答 4

6

在运行时创建动态查询的最灵活方法是使用 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();
于 2013-06-14T11:20:10.863 回答
1

您可以为此使用 PredicateBuilder:

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

 if (bCondition)
 {
  predicate = predicate.And(s => s == "that");
 }
于 2013-06-14T11:51:22.367 回答
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");

要使用,请下载示例包,然后获取 LinqSamples\DynamicQuery\DynamicQuery\Dynamic.cs 并与您的项目一起编译。

于 2013-06-14T13:20:23.627 回答
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; }
}
于 2013-06-14T11:24:57.570 回答