.net - statement - Linq to Entities-Cláusula "IN" de SQL
where in entity framework (8)
¿Seriamente? Ustedes nunca han usado
where (t.MyTableId == 1 || t.MyTableId == 2 || t.MyTableId == 3)
En T-SQL puedes tener una consulta como:
SELECT * FROM Users WHERE User_Rights IN ("Admin", "User", "Limited")
¿Cómo replicarías eso en una consulta de LINQ to Entities? ¿Es incluso posible?
Debe girarlo sobre su cabeza en términos de la forma en que lo piensa. En lugar de hacer "in" 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 en que encontraría un elemento en una lista regular en .NET.
Hay dos formas de hacer esto usando LINQ, una usa la sintaxis de consulta y la otra usa la sintaxis del método. Esencialmente, son los mismos 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 este caso podría ser la sintaxis del método porque, en lugar de asignar la variable, podría hacer la investigación 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;
}
De manera sintáctica, esto parece más complejo, y debe comprender el concepto de expresiones lambda o delegados para entender realmente lo que está sucediendo, pero como puede ver, esto condensa el código en una cantidad justa.
Todo se reduce a su estilo y preferencia de codificación: mis tres ejemplos hacen lo mismo de forma ligeramente diferente.
Una forma alternativa ni siquiera usa LINQ, puede usar la misma sintaxis de método que reemplaza "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;
}
Esta podría ser la forma en que puede usar directamente los métodos de extensión LINQ para verificar la cláusula en
var result = _db.Companies.Where(c => _db.CurrentSessionVariableDetails.Select(s => s.CompanyId).Contains(c.Id)).ToList();
Esto debería ser suficiente para su propósito. Compara dos colecciones y verifica si una colección tiene los valores que coinciden con los de la otra colección.
fea_Features.Where(s => selectedFeatures.Contains(s.feaId))
Iré por Inner Join en este contexto. Si hubiera utilizado contiene, iteraría 6 veces, a pesar de que el hecho de que solo haya 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!
Si está utilizando VS2008 / .net 3.5, consulte el consejo 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).
También traté de trabajar con algo similar a SQL-IN: consultar contra 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 manera de hacerlo en este momento.
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());
}
}
Trabajar con GUID en este contexto : como puede ver arriba, siempre hay la palabra "GUID" antes del GUID en los fragmentos de cadena de consulta. Si no agrega esto, ObjectQuery<T>.Where
lanza la siguiente excepción:
Los tipos de argumento ''Edm.Guid'' y ''Edm.String'' son incompatibles para esta operación., Casi igual a expresión, línea 6, columna 14.
Encontrado esto en los foros de MSDN, podría ser útil tenerlo en cuenta.
Matthias
... mirando hacia adelante para la próxima versión de .NET y Entity Framework, cuando todo mejore. :)
Un método alternativo a la respuesta de BenAlabaster.
En primer lugar, puede volver a escribir 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 para escribir, pero funciona igual.
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 lugar 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 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 plomerí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 intentar explicar este método, aparte de decir que esencialmente construye una expresión de predicado para todos los valores usando el valueSelector (es decir, p => p.User_Rights) y OR esos predicados juntos para crear una expresión para el completo predicado