使用 Linq 动态查询数据集

Dynamically Query a data set using Linq

我有一个要动态查询的数据集。如果我明确定义字段名称,此代码可以正常工作,但我不想这样做。我想使用字段名称的变量动态查询数据。我应该如何处理这个问题?

这个有效:

var dataField = "DemandStatusName";
var searchParam = form.GetValues("columns[" + j + "][search][value]").FirstOrDefault();

if(searchParam != "")
{
    dataSet = (from a in dataSet
               where a.DemandStatusName.Contains(searchParam)
               select a);
}

但这不是:

var dataField = "DemandStatusName";
var searchParam = form.GetValues("columns[" + j + "][search][value]").FirstOrDefault();

if(searchParam != "")
{
    dataSet = (from a in dataSet
               where dataField.Contains(searchParam)
               select a);
}

有必要使用LINQ吗?动态 SQL(带参数)会容易很多。

var dataField = "DemandStatusName";
var searchParam = form.GetValues("columns[" + j + "][search][value]").FirstOrDefault();
var sql = string.Format("select * from tableName where {0} = @param", dataField);

if(searchParam != "")
{
    dataSet = context.Database.SqlQuery<YourEntity>(sql,
                  new SqlParameter("param", searchParam));
}

How should I be approaching this?

LINQ 的主要好处之一是您可以获得类型安全。通过动态查询,您本质上失去了这个好处。虽然不是真的 "dynamic",但您可以通过编写包含您要查询的特定字段的扩展方法来保持这一优势:

public static IQueryable<MyTable> WhereContains(this IQueryable<MyTable> source, string field, string value)
{
  switch (field)
  {
    case nameof(MyTable.SomeField):
      return source.Where(a => a.SomeField.Contains(value));
    case nameof(MyTable.SomeOtherField):
      return source.Where(a => a.SomeOtherField.Contains(value));
    // ... etc
    default:
      throw new ArgumentOutOfRangeException($"Unexpected field {field}");
  }
}

这样,您的代码可以调用以下内容:

var dataField = "DemandStatusName";
var searchParam = form.GetValues("columns[" + j + "][search][value]").FirstOrDefault();

dataset.WhereContains(dataField, searchParam).OrderBy(a => a.Whatever)

也就是说,要从字面上回答您的问题,从技术上讲,您可以动态构建 LINQ 表达式。这可能看起来像这样,但请注意性能会很差,意外值可能会破坏它 and/or 会带来一些安全漏洞,尤其是当它们来自用户输入时:

var table = Expression.Parameter(typeof(MyTable));
var property = Expression.PropertyOrField(table, dataField);
var param = Expression.Constant(searchParam);
var contains = Expression.Call(property, "Contains", Type.EmptyTypes, searchParam);
var expression = Expression.Lambda<Func<MyTable,bool>>(contains, table);

var result = dataset.Where(expression);