在实体框架中动态添加where子句

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在实体框架中动态添加where子句相关的知识,希望对你有一定的参考价值。

我有此sql语句

SELECT userID from users WHERE
(name='name1' AND username='username1') OR
(name='name2' AND username='username2') OR
(name='name3' AND username='username3') OR
..........
(name='nameN' AND username='usernameN')

如何使用LINQ在实体框架中实现此语句?

答案

您可以使用美丽的东西PredicateBuilder。像这样使用它

var pr = PredicateBuilder.False<User>();
foreach (var name in names)
{
    pr = pr.Or(x => x.Name == name && x.Username == name);
}
return query.AsExpandable().Where(pr);
另一答案
 Expression<Func<User, bool>> whereExpression = null;
 foreach (var name in names)
 {
     Expression<Func<User, bool>> e1 = u => u.Name == name;
     Expression<Func<User, bool>> andExpression = e1.And(u => u.Username == name);
     whereExpression = whereExpression == null ? andExpression : whereExpression.Or(andExpression);
 }
 return query.Where(whereExpression);

此助手可能会帮助您。

public static class ExpressionExtensions
{
    public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> leftExpression, Expression<Func<T, bool>> rightExpression)
    {
        if (leftExpression == null) return rightExpression;
        if (rightExpression == null) return leftExpression;
        var paramExpr = Expression.Parameter(typeof(T));
        var exprBody = Expression.And(leftExpression.Body, rightExpression.Body);
        exprBody = (BinaryExpression)new ParameterReplacer(paramExpr).Visit(exprBody);

        return Expression.Lambda<Func<T, bool>>(exprBody, paramExpr);
    }

    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> leftExpression, Expression<Func<T, bool>> rightExpression)
    {
        if (leftExpression == null) return rightExpression;
        if (rightExpression == null) return leftExpression;
        var paramExpr = Expression.Parameter(typeof(T));
        var exprBody = Expression.Or(leftExpression.Body, rightExpression.Body);
        exprBody = (BinaryExpression)new ParameterReplacer(paramExpr).Visit(exprBody);

        return Expression.Lambda<Func<T, bool>>(exprBody, paramExpr);
    }
}

class ParameterReplacer : ExpressionVisitor
{
    private readonly ParameterExpression _parameter;

    protected override Expression VisitParameter(ParameterExpression node)
    {
        return base.VisitParameter(_parameter);
    }

    internal ParameterReplacer(ParameterExpression parameter)
    {
        _parameter = parameter;
    }
}
另一答案

注意:这是我所拥有的东西进行的修改,因此可能无法立即使用。但这将是一个很好的起点。

    public static IQueryable<TEntity> Where<TEntity>(this IQueryable<TEntity> source,
                                                     IEnumerable<WhereSpecifier> orClauses)
        where TEntity : class
    {
        if (!orClauses.Any()) return source.Where(t => false);
        Type type = typeof (TEntity);
        ParameterExpression parameter = null;
        Expression predicate = Expression.Constant(false, typeof (bool));
        ParameterExpression whereEnt = Expression.Parameter(type, "WhereEnt");
        foreach (WhereSpecifier orClause in orClauses)
        {
            Expression selector;
            if (orClause.Selector != null)
            {
                selector = orClause.Selector;
                parameter = orClause.Parameter;
            }
            else
            {
                parameter = whereEnt;
                Type selectorResultType;
                selector = GenerateSelector<TEntity>(parameter, orClause.Column, out selectorResultType);
            }
            Expression clause = selector.CallMethod(orClause.Method, 
                MakeConstant(selector.Type, orClause.Value), orClause.Modifiers);
            predicate = Expression.Or(predicate, clause);
        }

        var lambda = Expression.Lambda(predicate, whereEnt);
        var resultExp = Expression.Call(typeof (Queryable), "Where", new[] {type},
            source.Expression, Expression.Quote(lambda));
        return source.Provider.CreateQuery<TEntity>(resultExp);
    }

GenerateSelector:

public static Expression GenerateSelector<TEntity>(ParameterExpression parameter, string propertyName,
                                                         out Type resultType) where TEntity : class
    {
        //  create the selector part, but support child properties
        PropertyInfo property;
        Expression propertyAccess;
        if (propertyName.Contains('.'))
        {
            // support to be sorted on child fields.
            String[] childProperties = propertyName.Split('.');
            property = typeof (TEntity).GetProperty(childProperties[0]);
            propertyAccess = Expression.MakeMemberAccess(parameter, property);
            for (int i = 1; i < childProperties.Length; i++)
            {
                property = property.PropertyType.GetProperty(childProperties[i]);
                propertyAccess = Expression.MakeMemberAccess(propertyAccess, property);
            }
        }
        else
        {
            property = typeof (TEntity).GetProperty(propertyName);
            propertyAccess = Expression.MakeMemberAccess(parameter, property);
        }
        resultType = property.PropertyType;
        return propertyAccess;
    }

WHe​​reSpecifier:

public class WhereSpecifier
{
    public WhereSpecifier(string column, CheckMethod method, string value, CheckMethodModifiers modifiers)
    {
        Modifiers = modifiers;
        Value = value;
        Column = column;
        Method = method;
    }

    public WhereSpecifier(string column, CheckMethod method, string value)
        : this(column, method, value, CheckMethodModifiers.None)
    {
    }
    public Expression Selector { get; set; }
    public ParameterExpression Parameter { get; set; }
    public string Column { get; set; }
    public CheckMethod Method { get; set; }
    public CheckMethodModifiers Modifiers { get; set; }
    public string Value { get; set; }
}

用法:

var column = typeof(TEntity).Name + "ID";
var where = from id in SelectedIds 
            select new WhereSpecifier(column, CheckMethod.Equal, id.ToString());
return GetTable().Where(where);
另一答案

不要忘记,实体框架也可以理解entity sql,因此您可以在字符串中进行查询的这一部分。当您需要处理动态内容时,构建字符串非常方便。

另一答案

我尝试了@Egor Pavlikhin解决方案,但得到了"The LINQ expression node type 'Invoke' is not supported in LINQ to Entities."

根据this,您可以使用PredicateExtensions

var predicate = PredicateExtensions.Begin<User>();
foreach (var name in names)
{
    pr = pr.Or(x => x.Name == name);
}
return _context.Users.Where(predicate);
另一答案

我必须根据用户界面选择动态构建'Where'子句的谓词。 'System.Dynamic.Linq'允许使用字符串谓词。

foreach (var name in names)
{
    query = query.Where("Name=@0 And UserName=@1", name, name);
}
return query;

'System.Dynamic.Linq'作为nuget包提供。查阅Scott Guthrie对主题here的介绍。

另一答案

我发现这种方式太简单了:

    var query = context.InvoiceHeader.Where( i =>  i.DateInvoice >= model.datedu && i.DateInvoice <= model.dateau).AsQueryable();
        if(model.

以上是关于在实体框架中动态添加where子句的主要内容,如果未能解决你的问题,请参考以下文章

自动将一些 Where 子句添加到 Linq 表达式树

动态 SQL 到 LINQ 实体框架

C# 实体框架 OrderBy Children's Children's with a where 子句

实体框架 where 子句从特定列过滤

实体框架:Count() 在大型 DbSet 和复杂的 WHERE 子句上非常慢

实体框架 Linq 查询:.Where 链 vs &&