具有多个/未知标准的动态linq查询

我希望实现一个系统,通过该系统“构建”条件,然后从数据库返回结果数据。 目前,有一个存储过程可以动态生成SQL并执行它。 这是我想要删除的特殊问题。

我的问题来自这样一个事实:我可以在我的标准中包含多个字段,并且对于每个字段,可能有一个或多个值,具有不同的潜在运算符。

例如,

from t in Contacts where t.Email == "email@domain.com" || t.Email.Contains ("mydomain") where t.Field1 == "valuewewant" where t.Field2 != "valuewedontwant" select t 

字段,条件和运算符存储在数据库(和List )中,并且可能是这样的(基于上面);

 Email, Equals, "email@domain.com" Email, Contains, "mydomain" Field1, Equals, "valuewewant" Field2, DoesNotEqual, "valuewedontwant" 

要么

 new FieldCriteria { FieldName = "Email", Operator = 1, Value = "email@mydomain.com" } 

因此,使用我拥有的信息,我希望能够构建具有任意数量条件的查询。 我之前看到过Dynamic Linq和PredicateBuilder的链接,但我无法将其视为我自己问题的解决方案。

任何建议,将不胜感激。

更新

根据关于Dynamic Linq的建议,我提出了一个非常基本的解决方案,使用单个运算符,具有2个字段和多个条件。 目前在LinqPad中编码的有点粗糙,但结果正是我想要的;

 enum Operator { Equals = 1, } class Condition { public string Field { get; set; } public Operator Operator { get; set;} public string Value { get; set;} } void Main() { var conditions = new List(); conditions.Add(new Condition { Field = "Email", Operator = Operator.Equals, Value = "email1@domain.com" }); conditions.Add(new Condition { Field = "Email", Operator = Operator.Equals, Value = "email2@domain.com" }); conditions.Add(new Condition { Field = "Field1", Operator = Operator.Equals, Value = "Chris" }); var statusConditions = "Status = 1"; var emailConditions = from c in conditions where c.Field == "Email" select c; var field1Conditions = from c in conditions where c.Field == "Field1" select c; var emailConditionsFormatted = from c in emailConditions select string.Format("Email=\"{0}\"", c.Value); var field1ConditionsFormatted = from c in field1Conditions select string.Format("Field1=\"{0}\"", c.Value); string[] conditionsArray = emailConditionsFormatted.ToArray(); var emailConditionsJoined = string.Join("||", conditionsArray); Console.WriteLine(String.Format("Formatted Condition For Email: {0}",emailConditionsJoined)); conditionsArray = field1ConditionsFormatted.ToArray(); var field1ConditionsJoined = string.Join("||", conditionsArray); Console.WriteLine(String.Format("Formatted Condition For Field1: {0}",field1ConditionsJoined)); IQueryable results = ContactView.Where(statusConditions); if (emailConditions != null) { results = results.Where(emailConditionsJoined); } if (field1Conditions != null) { results = results.Where(field1ConditionsJoined); } results = results.Select("id"); foreach (int id in results) { Console.WriteLine(id.ToString()); } } 

用SQL生成的;

 -- Region Parameters DECLARE @p0 VarChar(1000) = 'Chris' DECLARE @p1 VarChar(1000) = 'email1@domain.com' DECLARE @p2 VarChar(1000) = 'email2@domain.com' DECLARE @p3 Int = 1 -- EndRegion SELECT [t0].[id] FROM [Contacts].[ContactView] AS [t0] WHERE ([t0].[field1] = @p0) AND (([t0].[email] = @p1) OR ([t0].[email] = @p2)) AND ([t0].[status] = @p3) 

和控制台输出:

 Formatted Condition For Email: Email="email1@domain.com"||Email="email2@domain.com" Formatted Condition For Field1: Field1="Chris" 

只需要清理它并添加其他操作符,它看起来不错。

如果到目前为止有人对此有任何意见,我们将不胜感激

我认为Dynamic LINQ将是一个选择。 DLINQ允许您将LINQ查询的一部分指定为“字符串”,然后DLINQ将该字符串编译为表达式树,以便传递给基础LINQ提供程序。 您的需求也是相同的,即您需要在运行时创建表达式树。

我建议你将FieldCriteria的属性Operator作为Enum来表示所有必需的操作(等于,小于等)。 然后你需要编写一个函数,它接受一个FieldCriteria列表并返回一个“表达式”字符串,然后可以将其输入DLINQ以获取表达式树。

LINQ的技巧是从数据构建Expression 。 举个例子,举例说明:

 var param = Expression.Parameter(typeof(MyObject), "t"); var body = Expression.Or( Expression.Equal(Expression.PropertyOrField(param, "Email"), Expression.Constant("email@domain.com")), Expression.Call(Expression.PropertyOrField(param, "Email"), "Contains", null, Expression.Constant("mydomain")) ); body = Expression.AndAlso(body, Expression.Equal(Expression.PropertyOrField(param, "Field1"), Expression.Constant("valuewewant"))); body = Expression.AndAlso(body, Expression.NotEqual(Expression.PropertyOrField(param, "Field2"), Expression.Constant("valuewedontwant"))); var lambda = Expression.Lambda>(body, param); var data = source.Where(lambda); 

特别要注意AndAlso如何用于组合各种操作(与多个Where相同,但更简单)。

这可以通过Linq简单地完成,您可以将其他运算符附加到查询对象。 这是一个例子。

 query = db.Contacts.Where( ... ); query = query.Where( ... ); query = query.Where( ... ); 

这是一个更简单,更简短的解决方案。