Solution de contournement ‘Contains ()’ utilisant Linq to Entities?

J’essaie de créer une requête qui utilise une liste d’id dans la clause where, en utilisant l’api du client Silverlight ADO.Net Data Services (et donc Linq To Entities). Est-ce que quelqu’un sait d’une solution de contournement à ne pas être pris en charge?

Je veux faire quelque chose comme ça:

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

J’ai essayé ceci:

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

Mais obtenu “La méthode ‘Any’ n’est pas supscope”.

Mise à jour: EF ≥ 4 prend en charge Contains directement (Checkout Any ), vous n’avez donc pas besoin de solution de contournement.

 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); } 

USAGE:

 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" ); } } 

Vous pouvez vous appuyer sur le codage de certains e-sql (notez le mot-clé “it”):

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

Voici le code que j’ai utilisé pour générer des e-sql à partir d’une collection, YMMV:

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

De 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); } 

et la requête devient:

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

Je ne suis pas sûr à propos de Silverligth, mais dans linq to objects, j’utilise toujours any () pour ces requêtes.

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

Pour compléter le dossier, voici le code que j’ai finalement utilisé (vérification des erreurs omise pour plus de clarté) …

 // 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); } 

Voici un exemple où je montre comment écrire des requêtes basées sur des ensembles à l’aide de DataServiceContext: http://blogs.msdn.com/phaniraj/archive/2008/07/17/set-based-operations-in-ado-net-data -services.aspx

Merci beaucoup. La méthode d’extension WhereIn me suffisait. Je l’ai profilé et généré la même commande SQL sur la firebase database que e-sql.

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

A généré ceci:

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

Je pense qu’une jointure dans LINQ peut être une démonstration.

Je n’ai pas testé le code cependant. J’espère que cela aide. À votre santé. 🙂

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

Rejoindre LINQ:

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

Désolé nouvel utilisateur, j’aurais commenté la réponse réelle, mais il semble que je ne peux pas encore le faire?

Quoi qu’il en soit, en ce qui concerne la réponse avec un exemple de code pour BuildContainsExpression (), sachez que si vous utilisez cette méthode sur des entités de firebase database (et non des objects en mémoire) et que vous utilisez IQueryable, car il fait fondamentalement beaucoup de conditions SQL “ou” pour vérifier la clause “where in” (exécutez-la avec SQL Profiler pour voir).

Cela peut signifier que si vous affinez un object IQueryable avec plusieurs BuildContainsExpression (), il ne le transformera pas en une instruction SQL qui sera exécutée à la fin comme prévu.

La solution de contournement pour nous consistait à utiliser plusieurs jointures LINQ pour conserver un seul appel SQL.

En plus de la réponse sélectionnée.

Remplacez Expression.Or par Expression.OrElse à utiliser avec Nhibernate et corrigez Unable to cast object of type 'NHibernate.Hql.Ast.HqlBitwiseOr' to type 'NHibernate.Hql.Ast.HqlBooleanExpression' transtyper l’ Unable to cast object of type 'NHibernate.Hql.Ast.HqlBitwiseOr' to type 'NHibernate.Hql.Ast.HqlBooleanExpression' exception Unable to cast object of type 'NHibernate.Hql.Ast.HqlBitwiseOr' to type 'NHibernate.Hql.Ast.HqlBooleanExpression' .