Linq to Entities – cláusula SQL “IN”

En T-SQL puede 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?

Debes volverte loco en términos de la manera en que lo estás pensando. En lugar de hacer “en” para encontrar los derechos de usuario del elemento actual en un conjunto predefinido de derechos de usuario aplicables, está solicitando un conjunto predefinido de derechos de usuario si contiene el valor aplicable del elemento actual. Esta es exactamente la misma forma que encontrarías un elemento en una lista regular en .NET.

Hay dos formas de hacerlo usando LINQ, uno usa la syntax de la consulta y el otro usa la syntax del método. Básicamente, son lo mismo y podrían usarse indistintamente según 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 syntax 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 debes entender el concepto de expresiones lambda o delegates para realmente descifrar qué está pasando, pero como puedes ver, esto condensa el código bastante.

Todo se reduce a tu estilo y preferencia de encoding: los tres ejemplos hacen lo mismo de forma ligeramente diferente.

Una forma alternativa ni siquiera utiliza LINQ, puede usar la misma syntax de método que reemplaza “donde” por “FindAll” y obtiene 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; } 

Esto debería ser suficiente para su 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)) 

Si usa VS2008 / .net 3.5, consulte la sugerencia n. ° 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, simplemente use el método array.Contains (someEntity.Member).

Iré por Inner Join en este contexto. Si hubiera usado “contains”, iteraría 6 veces a pesar de que haya una sola 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 Contiene, buscará cada elemento de la Lista 1 en la Lista 2, lo que significa que la iteración ocurrirá 49 veces.

Esta podría ser la forma posible en 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(); 

También traté de trabajar con algo similar a SQL-IN: consultar con un modelo de datos de entidad . Mi enfoque es un generador de cadenas para componer una gran expresión OR. Eso es terriblemente feo, pero me temo que es la única forma de ir ahora mismo.

Ahora bien, eso se ve así:

 Queue productIds = new Queue(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()); } } 

Trabajar con GUID en este contexto : Como puede ver arriba, siempre hay la palabra “GUID” antes del guid ifself en los fragmentos de cadena de consulta. Si no agrega esto, ObjectQuery.Where arroja la siguiente excepción:

Los tipos de argumento ‘Edm.Guid’ y ‘Edm.String’ son incompatibles para esta operación., Expresión casi igual, línea 6, columna 14.

Encontré esto en los foros de MSDN, podría ser útil tenerlo en cuenta.

Matthias

… esperando la próxima versión de .NET y Entity Framework, cuando todo sea mejor. 🙂

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 ‘prolijo’ y un dolor de escribir, pero funciona de todos modos.

Entonces, si tuviéramos algún método de utilidad que facilitara la creación de este tipo de expresiones LINQ, estaríamos en el negocio.

con un método de utilidad en su lugar, puede escribir algo como esto:

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

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

 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> BuildOrExpression( Expression> valueSelector, IEnumerable 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( (accumulate, equal) => Expression.Or(accumulate, equal) ); return Expression.Lambda>(body, p); } 

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

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

¿Seriamente? Ustedes nunca han usado

 where (t.MyTableId == 1 || t.MyTableId == 2 || t.MyTableId == 3)