我希望实现一个系统,用于“构建”条件,然后从数据库返回结果数据。目前,有一个存储过程可以动态生成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<FieldCriteria>
),并且会像这样(基于上面的内容);
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<Condition>();
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"
只需要清理它并添加其他操作符,它看起来很好。
如果到目前为止有人对此有任何意见,我们将不胜感激任何意见
答案 0 :(得分:9)
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<Func<MyObject, bool>>(body, param);
var data = source.Where(lambda);
请特别注意AndAlso
如何用于撰写各种操作(与多个Where
相同,但更简单)。
答案 1 :(得分:1)
我认为Dynamic LINQ将是一个选择。 DLINQ允许您将LINQ查询的一部分指定为“字符串”,然后DLINQ将该字符串编译为表达式树,以便传递给基础LINQ提供程序。您的需求也是相同的,即您需要在运行时创建表达式树。
我建议你将Operator
中的属性FieldCriteria
作为Enum
表示所有必需的操作(等于,少于等)。然后你需要编写一个带有FieldCriteria
列表的函数并返回一个“表达式”字符串,然后可以将其输入DLINQ以获取表达式树。
答案 2 :(得分:-2)
这可以通过Linq简单地完成,您可以将其他运算符附加到查询对象。这是一个例子。
query = db.Contacts.Where( ... );
query = query.Where( ... );
query = query.Where( ... );
这是一个更简单,更简短的解决方案。