Linq to Entities-Cláusula SQL "IN"


En T-SQL podrías tener una consulta como:

SELECT * FROM Users WHERE User_Rights IN ("Admin", "User", "Limited")

¿Cómo replicaría eso en una consulta de LINQ to Entities? ¿Es posible?

Author: shA.t, 2009-05-13

8 answers

Necesitas darle la vuelta a su cabeza en términos de la forma en que lo estás pensando. En lugar de hacer "in" para encontrar los derechos de usuario del elemento actual en un conjunto predefinido de derechos de usuario aplicables, está preguntando a un conjunto predefinido de derechos de usuario si contiene el valor aplicable del elemento actual. Esto es exactamente de la misma manera que usted encontraría un elemento en una lista regular en. NET.

Hay dos formas de hacer esto usando LINQ, una usa sintaxis de consulta y la otra usa sintaxis de método. Esencialmente, son los mismos y podrían usarse indistintamente dependiendo de su preferencia:

Sintaxis de Consulta:

var selected = from u in users
               where new[] { "Admin", "User", "Limited" }.Contains(u.User_Rights)
               select u

foreach(user u in selected)
{
    //Do your stuff on each selected user;
}

Sintaxis del Método:

var selected = users.Where(u => new[] { "Admin", "User", "Limited" }.Contains(u.User_Rights));

foreach(user u in selected)
{
    //Do stuff on each selected user;
}

Mi preferencia personal en esta instancia podría ser la sintaxis del método porque en lugar de asignar la variable, podría hacer el foreach sobre una llamada anónima como esta:

foreach(User u in users.Where(u => new [] { "Admin", "User", "Limited" }.Contains(u.User_Rights)))
{
    //Do stuff on each selected user;
}

Sintácticamente esto parece más complejo, y hay que entender el concepto de expresiones lambda o delegados para realmente averiguar lo que está pasando, pero como se puede ver, esto condensa el código una buena cantidad.

Todo se reduce a su estilo de codificación y preferencia - los tres de mis ejemplos hacen lo mismo ligeramente diferente.

Una forma alternativa ni siquiera usa LINQ, puede usar la misma sintaxis de método reemplazando "where" con "findAll" y obtener el mismo resultado, que también funcionará en. NET 2.0:

foreach(User u in users.FindAll(u => new [] { "Admin", "User", "Limited" }.Contains(u.User_Rights)))
{
    //Do stuff on each selected user;
}
 324
Author: BenAlabaster,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-07 11:05:48

Esto debería ser suficiente para tu propósito. Compara dos colecciones y comprueba si una colección tiene los valores que coinciden con los de la otra colección

fea_Features.Where(s => selectedFeatures.Contains(s.feaId))
 18
Author: Balaji Birajdar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-27 13:50:48

Si está utilizando VS2008/. net 3.5, consulte el consejo #8 de Alex James: http://blogs.msdn.com/alexj/archive/2009/03/26/tip-8-writing-where-in-style-queries-using-linq-to-entities.aspx

De lo contrario solo use el array.Contiene(someEntity.Miembro) método.

 9
Author: KristoferA,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-06 16:01:31

Iré por la Unión Interior en este contexto. Si hubiera utilizado contiene, iteraría 6 veces a pesar del hecho de que solo hay una coincidencia.

var desiredNames = new[] { "Pankaj", "Garg" }; 

var people = new[]  
{  
    new { FirstName="Pankaj", Surname="Garg" },  
    new { FirstName="Marc", Surname="Gravell" },  
    new { FirstName="Jeff", Surname="Atwood" }  
}; 

var records = (from p in people join filtered in desiredNames on p.FirstName equals filtered  select p.FirstName).ToList(); 

Desventajas de Contiene

Supongamos que tengo dos objetos de lista.

List 1      List 2
  1           12
  2            7
  3            8
  4           98
  5            9
  6           10
  7            6

Usando Contains, buscará cada elemento de la Lista 1 en la Lista 2, lo que significa que la iteración ocurrirá 49 veces.!!

 5
Author: Pankaj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-04-03 14:19:38

Esta podría ser la forma en la que puede usar directamente los métodos de extensión LINQ para verificar la cláusula in

var result = _db.Companies.Where(c => _db.CurrentSessionVariableDetails.Select(s => s.CompanyId).Contains(c.Id)).ToList();
 3
Author: Torakami,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-30 06:30:55

También intenté trabajar con un SQL-IN-like thing - consultando contra un Entity Data Model. Mi enfoque es un constructor de cadenas para componer una expresión OR grande. Eso es terriblemente feo, pero me temo que es la única manera de ir ahora mismo.

Ahora bien, eso se ve así:

Queue<Guid> productIds = new Queue<Guid>(Products.Select(p => p.Key));
if(productIds.Count > 0)
{
    StringBuilder sb = new StringBuilder();
    sb.AppendFormat("{0}.ProductId = Guid\'{1}\'", entities.Products.Name, productIds.Dequeue());
    while(productIds.Count > 0)
    {
        sb.AppendFormat(" OR {0}.ProductId = Guid\'{1}\'",
          entities.Products.Name, productIds.Dequeue());
    }
}

Trabajando con GUID en este contexto: Como puedes ver arriba, siempre hay la palabra "GUID" antes del GUID ifself en los fragmentos de cadena de consulta. Si no agrega esto, ObjectQuery<T>.Where lanza el siguiente excepción:

Los tipos de argumento 'Edm.Guid' y Edm.String ' son incompatibles para esto operación., casi igual expresión, línea 6, columna 14.

Encontrado esto en los foros de MSDN, podría ser útil tener en cuenta.

Matthias

... esperamos la próxima versión de. NET y Entity Framework, cuando todo mejore. :)

 2
Author: Matthias Meid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-10 08:34:08

Un método alternativo a la respuesta de BenAlabaster

En primer lugar, puede reescribir la consulta de esta manera:

var matches = from Users in people
        where Users.User_Rights == "Admin" ||
              Users.User_Rights == "Users" || 
              Users.User_Rights == "Limited"
        select Users;

Ciertamente esto es más 'verboso' y un dolor de escribir, pero funciona de todos modos.

Así que si tuviéramos algún método de utilidad que hiciera fácil crear este tipo de expresiones LINQ estaríamos en el negocio.

Con un método de utilidad en su lugar se puede escribir algo como esto:

var matches = ctx.People.Where(
        BuildOrExpression<People, string>(
           p => p.User_Rights, names
        )
);

Esto construye una expresión que tiene el mismo efecto as:

var matches = from p in ctx.People
        where names.Contains(p.User_Rights)
        select p;

Pero lo que es más importante en realidad funciona contra.NET 3.5 SP1.

Aquí está la función de fontanería que hace esto posible:

public static Expression<Func<TElement, bool>> BuildOrExpression<TElement, TValue>(
        Expression<Func<TElement, TValue>> valueSelector, 
        IEnumerable<TValue> values
    )
{     
    if (null == valueSelector) 
        throw new ArgumentNullException("valueSelector");

    if (null == values)
        throw new ArgumentNullException("values");  

    ParameterExpression p = valueSelector.Parameters.Single();

    if (!values.Any())   
        return e => false;

    var equals = values.Select(value =>
        (Expression)Expression.Equal(
             valueSelector.Body,
             Expression.Constant(
                 value,
                 typeof(TValue)
             )
        )
    );
   var body = equals.Aggregate<Expression>(
            (accumulate, equal) => Expression.Or(accumulate, equal)
    ); 

   return Expression.Lambda<Func<TElement, bool>>(body, p);
}

No voy a tratar de explicar este método, aparte de decir que esencialmente construye una expresión de predicado para todos los valores utilizando el valueSelector (es decir, p => p. User_Rights) y ORs esos predicados juntos para crear una expresión para el predicado completo

Fuente: http://blogs.msdn.com/b/alexj/archive/2009/03/26/tip-8-writing-where-in-style-queries-using-linq-to-entities.aspx

 1
Author: Shaegorath,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-08 13:32:32

En Serio? Ustedes nunca han usado

where (t.MyTableId == 1 || t.MyTableId == 2 || t.MyTableId == 3)
 -11
Author: cjm30305,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-31 03:18:07