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 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:
嗨,穆克,
由于我是C#的新手,我仍然在苦苦挣扎,这对我不起作用。
var query = _ConfigFile.ConnectionMasterSection; for(int i = 0; i typeof(ConnectionMaster).GetProperty(filter[i].Attribute).Name == filter[i].Value); }
这是空的,我用这个
var query = _ConfigFile.ConnectionMasterSection; //Hard coded res.Where(q => q.category == filter[0].Value);
它按照我的预期工作。
嗨布莱恩沃茨,
我也尝试了你的代码,我收到了这个错误:“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>( Expression.Equal(param, constant), Expression.Parameter(typeof(Connection),"p"))); }
这里的错误是什么?
看看这个http://www.albahari.com/nutshell/predicatebuilder.aspx ,它允许强类型谓词构建,它可以非常好。 如果你想要实际的动态字符串构建谓词,那么你可以使用ScottGu提供的LINQ动态查询库 。
虽然我会在第二个选项之前推荐第一个选项,但两者都能达到你想要的效果。
允许你这样做:
var predicate = PredicateBuilder.True(); if(!string.IsNullOrEmpty(name)) predicate = predicate.And(p => p.name == name); ... var myResults = Context.MyLinTypeQueryTable.Where(predicate);
和更多。
干得好:
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子句。
您可以使用fluent接口并在每个条件中添加新的Where子句fpr。 就像是:
var result = from cus in customers select cus; if(!string.IsNullOrEmpty(name)) result= result.Where(p => p.Name == name);
编辑评论:
如果要查询内存中的集合,可以使用reflection检索属性。
private Customer[] GetCustomers(Dictionary 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通过reflection检索属性。
使用Linq2Sql这个方法将导致检索所有记录,然后使用reflection迭代它们。
我对Dynamic LINQ有很好的经验。
我将它用于一个可以过滤和排序服务器端的丰富HTML表。 服务器接收包含请求参数的请求,其中键是属性的名称(例如“Lastname”),值是属性需要排序的值(例如“Smith”)。 使用该信息,我构建了一个查询字符串,并将其传递给Dynamic LINQ的Where
方法。
粗略地说,您可以想到以下内容:
public static IQueryable Filter (this IQueryable query, Dictionary 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注入的攻击,但您不应该让它盲目地解析所有用户输入。
听起来你需要动态组合查询。
看看我对这个问题的回答 。
它解释了如何对编译器组成IQueryable
查询,以及如何添加动态元素。
编辑
下面是一个如何在IQueryable
之上动态构建Where
条件的示例:
// This method ANDs equality expressions for each property, like so: // // customers.Where(c => c.Property1 == value1 && c.Property2 == value2 && ...); private IQueryable FilterQuery(IQueryable customers, IDictionary 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>(filterExpression, parameter); customers = customers.Where(whereBody); } return customers; }