LINQ中的动态查询

时间:2009-03-07 07:12:40

标签: c# linq

如果我说Customer字段包含字段:

,如何为Linq编写动态查询
string name
string address
int phoneno

我必须根据类似于

的信息进行查询
query = string.Empty;

if(!string.IsNullorEmpty(name))
{
   query += "@name = name";
}

if(!string.IsNullorEmpty(address))
{
   query += "@address = address";
}

if(!string.IsNullorEmpty(phoneno))
{
   query += "@phoneno = phoneno";
}

var result = from condition in customer
    where(query)
    select condition;

编辑#1:

这些项目在运行时可以改变,如

private Customer[] GetCustomers(Dictionary<string,string> attributes)
{
   here the attribute may be, name alone, or name and address, or name address and phoneno


      foreach(string field in attributes.key)
      {
           query += field == attributes[key];

      }

         Customers[] =ExecuteQuery(query);

}

LINQ是否支持这种查询?

编辑#2:

嗨Mouk,
由于我是C#的新手,我仍在苦苦挣扎,这对我不起作用。

var query = _ConfigFile.ConnectionMasterSection;

for(int i = 0; i < filter.count; i++)
{
    query = result.Where(p => typeof(ConnectionMaster).GetProperty(filter[i].Attribute).Name == filter[i].Value);
}

这是空的,我用的是

var query = _ConfigFile.ConnectionMasterSection;

//Hard coded
res.Where(q => q.category == filter[0].Value);

它按预期工作。

嗨Bryan Watts,
我也尝试了你的代码,我收到了这个错误:“Lambda参数不在范围内”。

for(int i = 0; i < filter.count; i++)
{
    Field item = filter[i];

    MemberExpression param = Expression.MakeMemberAccess(Expression.Parameter(typeof(Connection), "p"), typeof(Connection).GetProperty(item.Attribute));

    MemberExpression constant = Expression.MakeMemberAccess(Expression.Constant(item), typeof(Field).GetProperty("Value"));
}


try
{
    var myquery = Queryable.Where(coll, Expression.Lambda<Func<Connection, bool>>(
    Expression.Equal(param, constant), Expression.Parameter(typeof(Connection),"p")));
}

这里的错误是什么?

5 个答案:

答案 0 :(得分:5)

查看此http://www.albahari.com/nutshell/predicatebuilder.aspx,它允许强类型谓词构建,它可以非常好。如果你想要实际的动态字符串构建谓词,那么你可以使用ScottGu提供的LINQ Dynamic Query Library

虽然我会在第二个选项之前推荐第一个选项,但两者都能达到你想要的效果。

允许你这样做:

var predicate = PredicateBuilder.True<MyLinqType>();

if(!string.IsNullOrEmpty(name))
    predicate = predicate.And(p => p.name == name);


...

var myResults = Context.MyLinTypeQueryTable.Where(predicate);

还有更多。

答案 1 :(得分:3)

你走了:

var result = from customer in Customers
             where string.IsNullOrEmpty(phoneNo) || customer.PhoneNo == phoneNo
             where string.IsNullOrEmpty(address) || customer.Address == address
             select customer;

如果您担心这会在下面生成最佳SQL查询,那么您应该像往常一样附加SQL查询分析器并进行检查。但我相信Linq To Sql中的表达式解析器将根据参数的值合适地折叠where子句。

答案 2 :(得分:0)

您可以使用fluent接口并在每个条件下添加一个新的Where子句fpr。类似的东西:

 var result = from cus in customers select cus;
 if(!string.IsNullOrEmpty(name))
         result= result.Where(p => p.Name == name);

编辑评论:

如果要查询内存中的集合,可以使用反射检索属性。

private Customer[] GetCustomers(Dictionary<string,string> attributes)
{
      var result = from cus in customers select cus;    

      foreach(string key in attributes.Keys)
             result= result.Where(p => GetProperty(p, key )== attributes[key]);

         return result.ToList();    
}

假设GetProperty通过反射检索属性。

使用Linq2Sql这个方法将导致检索所有记录,然后使用反射迭代它们。

答案 3 :(得分:0)

听起来你需要动态撰写查询。

请参阅my answerthis question

它解释了编译器如何组成对IQueryable<T>的查询,以及如何添加动态元素。

修改

以下是如何在Where之上动态构建一组IQueryable<Customer>条件的示例:

// This method ANDs equality expressions for each property, like so:
//
// customers.Where(c => c.Property1 == value1 && c.Property2 == value2 && ...);

private IQueryable<Customer> FilterQuery(IQueryable<Customer> customers, IDictionary<string, string> filter)
{
    var parameter = Expression.Parameter(typeof(Customer), "c");
    Expression filterExpression = null;

    foreach(var filterItem in filter)
    {
        var property = typeof(Customer).GetProperty(filterItem.Key);
        var propertyAccess = Expression.MakeMemberAccess(parameter, property);
        var equality = Expression.Equal(propertyAccess, Expression.Constant(filterItem.Value));

        if(filterExpression == null)
        {
            filterExpression = equality;
        }
        else
        {
            filterExpression = Expression.And(filterExpression, equality);
        }
    }

    if(filterExpression != null)
    {
        var whereBody = Expression.Lambda<Func<Customer, bool>>(filterExpression, parameter);

        customers = customers.Where(whereBody);
    }

    return customers;
}

答案 4 :(得分:0)

我对Dynamic LINQ有很好的经验。

我将它用于丰富的HTML表格,可以对服务器端进行过滤和排序。服务器接收包含请求参数的请求,其中键是属性的名称(例如“Lastname”),值是属性需要排序的值(例如“Smith”)。使用该信息,我构建了一个查询字符串,并将其传递给Dynamic LINQ的Where方法。

粗略地说,你可以想到以下内容:

public static IQueryable<T> Filter<T>(this IQueryable<T> query, Dictionary<string, string> dictionary)
{
    Type t = typeof(T);
    StringBuilder sb = new StringBuilder();
        PropertyInfo[] properties = t.GetProperties();
        foreach(string key in dictionary.Keys)
        {
            PropertyInfo property = properties.Where(p => p.Name == key).SingleOrDefault();
            if(property != null)
            {
                if (sb.Length > 0) sb.Append(" && ");

                string value = dictionary[key];

                sb.Append(string.Format(@"{0}.ToString().Contains(""{1}"")", key, value));
            }
        }

        if (sb.Length > 0)
        return query.Where(sb.ToString());
    else
            return query;
}

代码不在我的脑海中,因而未经过测试。

当然,这是最基本的版本:它进行简单的字符串比较。如果你想进行数值比较(意思是你想要的用户,其中UserID正好是100,而不是UserID.ToString().Contains("100")),或者查询嵌套的属性(例如Customer.Company.CompanyAddress),或查询收集更复杂。您还应该考虑安全性:虽然Dynamic LINQ不容易受到SQL注入的攻击,但您不应该让它盲目地解析所有用户输入。