【问题标题】:Dynamic linq query with multiple/unknown criteria具有多个/未知条件的动态 linq 查询
【发布时间】:2011-09-21 03:47:30
【问题描述】:

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

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

例如,

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"

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

到目前为止,如果有人对此有任何 cmet,任何输入将不胜感激

【问题讨论】:

    标签: c# linq dynamic


    【解决方案1】:

    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 相同,但更简单)。

    【讨论】:

    • 上面的代码是否不需要您知道字段/条件/运算符。在我的场景中。在编译时没有一个是已知的,并且在这方面它需要是完全动态的。请原谅我误会了,因为 Lambda 函数有点超出我目前的知识范围。
    • @Chris 不,但您必须编写一些代码来生成表达式
    • 如何在where子句中使用IN?
    【解决方案2】:

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

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

    【讨论】:

    • “LINQ to ”的整个想法是将静态 C# 编译时表达式转换为某种字符串,以发送到底层数据库执行。您建议使用将字符串转换为 linq 表达式然后再转换为字符串的建议似乎有点多余:)
    • 您对 SQL 的“静态”表达式是正确的,但在问题中,用户在编译时不知道表达式,因此您需要在运行时生成表达式。 LINQ 是从“字符串”中执行此操作的一种方法,另一种方法是使用表达式 API 在运行时创建所需的表达式。
    【解决方案3】:

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

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

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

    【讨论】:

    • 这是 AND 逻辑,但是 OR 逻辑呢?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-01-06
    • 1970-01-01
    • 2016-05-28
    • 2015-03-06
    • 2016-07-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多