linq 具有多个/未知条件的动态链接查询[重复]

9bfwbjaz  于 2022-12-06  发布在  其他
关注(0)|答案(4)|浏览(179)

此问题在此处已有答案

C# Dynamic database filtering with Linq Expression(3个答案)
两个月前关门了。
我希望实现一个系统,通过该系统,a使用“构建”条件,然后从数据库返回结果数据。目前,有一个存储过程,它生成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的链接,但无法将其视为解决我自己问题的解决方案。
如有任何建议,我们将不胜感激。
更新
根据关于动态Linq的建议,我想出了一个非常基本的解决方案,使用一个单一的操作符,两个字段和多个标准。

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"

只需要清理这个,并添加其他操作员,它看起来很好。
到目前为止,如果任何人对此有任何意见,任何投入都将不胜感激

luaexgnf

luaexgnf1#

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

fjaof16o

fjaof16o2#

我认为动态LINQ将是选项之一。DLINQ允许您将LINQ查询的一部分指定为“字符串”,然后DLINQ将该字符串编译为表达式树,以便传递给底层LINQ提供程序。您的需求也是相同的,即您需要在运行时创建表达式树。
我建议您将FieldCriteria中的Operator属性设置为Enum,它表示所有需要的运算(等于、小于等)。然后您需要编写一个函数,该函数接受FieldCriteria的列表并返回一个“expression”字符串,然后将该字符串输入DLINQ以获得表达式树。

vatpfxk5

vatpfxk53#

这听起来和我最近解决的一个问题非常相似。在我的例子中,我必须根据Sql中定义的复杂过滤器将对象过滤到不同的类别中。
我已经创建了一个Nuget包DynamicFilter.Sql来从基于sql的过滤器动态生成lambda表达式。这个包是开源的,可以在github上找到。
您可以像这样简单地使用它,

var filter = FilterExpression.Compile<User>("(Email = 'email@domain.com' or Email like '%@%mydomain.com') and deleted <> true ");

bool match = filter(new User {Email="alice@mydomain.com", Deleted=false}); //Matches true
neskvpey

neskvpey4#

这可以通过Linq简单地完成,在Linq中,您可以将附加的操作符附加到查询对象。

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

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

相关问题