2011-04-08 21 views
20

tengo esta instrucción SQLAñadir la cláusula where de forma dinámica en Entity Framework

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') 

Hho puedo poner en práctica esta declaración con el marco de la entidad utilizando LINQ?

+0

Esta publicación de blog de Syed Mehroz Alam contiene varios ejemplos que son muy minuciosos, y muestra cómo combinar la sintaxis de consulta LINQ con la sintaxis de método de una manera útil: [LINQ: cómo crear consultas complejas utilizando ejecución diferida y tipos anónimos] (http://smehrozalam.wordpress.com/2010/04/06/linq-how-to-build-complex-queries-utilizing-deferred-execution-and-anonymous-types/) –

Respuesta

26

Puede usar algo hermoso llamado PredicateBuilder. Úselo así

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); 
+1

He comprobado este código antes . por lo que recuerdo, este código no funciona con entidad, solo funciona con modelos de datos linq a sql. "No estoy seguro. ¿Has trabajado con eso?" – Mironline

+0

Sí, es compatible con EF. Lo uso incluso con nHibernate, es brillante. –

+2

dear HeavyWave: He comprobado nuevamente, pero obtuve esta excepción "LINQ to Entities no admite el tipo de nodo de expresión LINQ" Invoke ". ¿Tienes alguna idea sobre eso? – Mironline

2

No olvide que entity framework también comprende entity sql, por lo que puede hacer esta parte de la consulta en una cadena. Construir una cadena es muy conveniente cuando tienes cosas dinámicas que tienes que hacer.

4

NOTA: esto se ha modificado de algo que tengo, por lo que podría no funcionar fuera de la caja. Pero sería un buen punto de partida.

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; 
    } 

WHereSpecifier:

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; } 
} 

Uso:

var column = typeof(TEntity).Name + "ID"; 
var where = from id in SelectedIds 
      select new WhereSpecifier(column, CheckMethod.Equal, id.ToString()); 
return GetTable().Where(where); 
4
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); 

Este ayudante m ay ayudarte

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; 
    } 
} 
2

me trataron solución @Egor Pavlikhin pero Tengo "The LINQ expression node type 'Invoke' is not supported in LINQ to Entities.".

Según this puede utilizar PredicateExtensions:

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

que tenía que construir el predicado de la cláusula 'Dónde' dinámicamente basándose en las selecciones de interfaz de usuario. 'System.Dynamic.Linq' permite predicados a partir de cadenas.

foreach (var name in names) 
{ 
    query = query.Where("[email protected] And [email protected]", name, name); 
} 
return query; 

'System.Dynamic.Linq' está disponible como paquete nuget. Mira la introducción de Scott Guthrie al tema here.