‘Contiene ()’ solución alternativa utilizando Linq para Entidades?

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 txnIds = new List(); // 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(tt => tt == t.OpenTransactionId) select t; 

Pero obtuve “El método ‘Cualquiera’ no es compatible”.

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

 public static IQueryable WhereIn ( this ObjectQuery query, Expression> selector, IEnumerable 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 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>(body, p)); } //Optional - to allow static collection: public static IQueryable WhereIn ( this ObjectQuery query, Expression> selector, params TValue[] collection ) { return WhereIn(query, selector, (IEnumerable)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" ); } } 

Puede recurrir a la encoding 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) + "}"); 

Desde MSDN :

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

y la consulta se convierte en:

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

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(tt => tt.OpenTransactionId, txnIds)); // The function to build the contains expression static System.Linq.Expressions.Expression> BuildContainsExpression( System.Linq.Expressions.Expression> valueSelector, IEnumerable 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((accumulate, equal) => System.Linq.Expressions.Expression.Or(accumulate, equal)); return System.Linq.Expressions.Expression.Lambda>(body, p); } 

Aquí hay un ejemplo donde demuestro cómo escribir consultas basadas en conjuntos usando el DataServiceContext: http://blogs.msdn.com/phaniraj/archive/2008/07/17/set-based-operations-in-ado-net-data -services.aspx

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

Creo que un Join in LINQ puede ser un paseo.

Aunque no he probado el código. Espero eso ayude. Aclamaciones. 🙂

 List txnIds = new List(); // Fill list var q = from t in svc.OpenTransaction join tID in txtIds on t equals tID select t; 

Únete en LINQ:

http://weblogs.asp.net/salimfayad/archive/2008/07/09/linq-to-entities-join-queries.aspx

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 statement SQL que se ejecuta al final como espera.

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

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