query net metodos consultas c# linq entity-framework .net-3.5 linq-to-entities

c# - net - ''Contiene()'' solución alternativa utilizando Linq para Entidades?



linq.net core (10)

Intento crear una consulta que use una lista de identificadores en la cláusula where, utilizando la API del cliente ADO.Net Data Services de Silverlight (y, por lo tanto, Linq To Entities). ¿Alguien sabe de una solución alternativa a que Contains no es compatible?

Quiero hacer algo como esto:

List<long?> txnIds = new List<long?>(); // Fill list var q = from t in svc.OpenTransaction where txnIds.Contains(t.OpenTransactionId) select t;

Intenté esto:

var q = from t in svc.OpenTransaction where txnIds.Any<long>(tt => tt == t.OpenTransactionId) select t;

Pero obtuve "El método ''Cualquiera'' no es compatible".


Además de la respuesta seleccionada.

Reemplace Expression.Or con Expression.OrElse para usar con Nhibernate y corregir Unable to cast object of type ''NHibernate.Hql.Ast.HqlBitwiseOr'' to type ''NHibernate.Hql.Ast.HqlBooleanExpression'' excepción Unable to cast object of type ''NHibernate.Hql.Ast.HqlBitwiseOr'' to type ''NHibernate.Hql.Ast.HqlBooleanExpression'' .




Desde MSDN :

static Expression<Func<TElement, bool>> BuildContainsExpression<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(); // p => valueSelector(p) == values[0] || valueSelector(p) == ... 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); }

y la consulta se convierte en:

var query2 = context.Entities.Where(BuildContainsExpression<Entity, int>(e => e.ID, ids));


Lo siento nuevo usuario, habría comentado sobre la respuesta real, pero parece que no puedo hacer eso todavía?

De todos modos, en cuanto a la respuesta con código de ejemplo para BuildContainsExpression (), tenga en cuenta que si usa ese método en entidades de base de datos (es decir, no en objetos en memoria) y está utilizando IQueryable, realmente tiene que ir a la base de datos ya que básicamente hace un montón de SQL "o" condiciones para verificar la cláusula "where in" (ejecutarlo con SQL Profiler para ver).

Esto puede significar que, si está redefiniendo un IQueryable con múltiples BuildContainsExpression (), no lo convertirá en una declaración SQL que se ejecuta al final como espera.

La solución para nosotros fue utilizar varias combinaciones LINQ para mantenerlo en una llamada SQL.


Muchas gracias. Donde el método de extensión fue suficiente para mí. Lo perfilé y generé el mismo comando SQL para la Base de datos como e-sql.

public Estado[] GetSomeOtherMore(int[] values) { var result = _context.Estados.WhereIn(args => args.Id, values) ; return result.ToArray(); }

Generado esto:

SELECT [Extent1].[intIdFRLEstado] AS [intIdFRLEstado], [Extent1].[varDescripcion] AS [varDescripcion] FROM [dbo].[PVN_FRLEstados] AS [Extent1] WHERE (2 = [Extent1].[intIdFRLEstado]) OR (4 = [Extent1].[intIdFRLEstado]) OR (8 = [Extent1].[intIdFRLEstado])


No estoy seguro acerca de Silverligth, pero en linq a los objetos, siempre uso any () para estas consultas.

var q = from t in svc.OpenTranaction where txnIds.Any(t.OpenTransactionId) select t;


Para completar el registro, aquí está el código que finalmente utilicé (se omite la verificación de errores para mayor claridad) ...

// How the function is called var q = (from t in svc.OpenTransaction.Expand("Currency,LineItem") select t) .Where(BuildContainsExpression<OpenTransaction, long>(tt => tt.OpenTransactionId, txnIds)); // The function to build the contains expression static System.Linq.Expressions.Expression<Func<TElement, bool>> BuildContainsExpression<TElement, TValue>( System.Linq.Expressions.Expression<Func<TElement, TValue>> valueSelector, IEnumerable<TValue> values) { if (null == valueSelector) { throw new ArgumentNullException("valueSelector"); } if (null == values) { throw new ArgumentNullException("values"); } System.Linq.Expressions.ParameterExpression p = valueSelector.Parameters.Single(); // p => valueSelector(p) == values[0] || valueSelector(p) == ... if (!values.Any()) { return e => false; } var equals = values.Select(value => (System.Linq.Expressions.Expression)System.Linq.Expressions.Expression.Equal(valueSelector.Body, System.Linq.Expressions.Expression.Constant(value, typeof(TValue)))); var body = equals.Aggregate<System.Linq.Expressions.Expression>((accumulate, equal) => System.Linq.Expressions.Expression.Or(accumulate, equal)); return System.Linq.Expressions.Expression.Lambda<Func<TElement, bool>>(body, p); }


Puede recurrir a la codificación manual de algunos e-sql (tenga en cuenta la palabra clave "eso"):

return CurrentDataSource.Product.Where("it.ID IN {4,5,6}");

Aquí está el código que utilicé para generar algunos e-sql de una colección, YMMV:

string[] ids = orders.Select(x=>x.ProductID.ToString()).ToArray(); return CurrentDataSource.Products.Where("it.ID IN {" + string.Join(",", ids) + "}");


Actualización: EF ≥ 4 admite Contains directamente (Checkout Any ), por lo que no necesita ninguna solución.

public static IQueryable<TEntity> WhereIn<TEntity, TValue> ( this ObjectQuery<TEntity> query, Expression<Func<TEntity, TValue>> selector, IEnumerable<TValue> collection ) { if (selector == null) throw new ArgumentNullException("selector"); if (collection == null) throw new ArgumentNullException("collection"); if (!collection.Any()) return query.Where(t => false); ParameterExpression p = selector.Parameters.Single(); IEnumerable<Expression> equals = collection.Select(value => (Expression)Expression.Equal(selector.Body, Expression.Constant(value, typeof(TValue)))); Expression body = equals.Aggregate((accumulate, equal) => Expression.Or(accumulate, equal)); return query.Where(Expression.Lambda<Func<TEntity, bool>>(body, p)); } //Optional - to allow static collection: public static IQueryable<TEntity> WhereIn<TEntity, TValue> ( this ObjectQuery<TEntity> query, Expression<Func<TEntity, TValue>> selector, params TValue[] collection ) { return WhereIn(query, selector, (IEnumerable<TValue>)collection); }

USO:

public static void Main() { using (MyObjectContext context = new MyObjectContext()) { //Using method 1 - collection provided as collection var contacts1 = context.Contacts.WhereIn(c => c.Name, GetContactNames()); //Using method 2 - collection provided statically var contacts2 = context.Contacts.WhereIn(c => c.Name, "Contact1", "Contact2", "Contact3", "Contact4" ); } }