J'essaie de créer une requête qui utilise une liste d'identifiants dans la clause where, en utilisant l'api du client Silverlight ADO.Net Data Services (et donc Linq To Entities). Quelqu'un connaît-il une solution de contournement pour Contains qui n'est pas prise en charge?
Je veux faire quelque chose comme ça:
List<long?> txnIds = new List<long?>();
// 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<long>(tt => tt == t.OpenTransactionId)
select t;
Mais obtenu "La méthode 'Any' n'est pas supportée".
c#
linq
entity-framework
.net-3.5
linq-to-entities
James Bloomer
la source
la source
Réponses:
Mise à jour: EF ≥ 4 prend
Contains
directement en charge (CheckoutAny
), vous n'avez donc besoin d'aucune solution de contournement.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); }
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" ); } }
la source
if (!collection.Any()) //action;
action - replace par simplement renvoyer une requête vide du type demandé pour de meilleures performances - ou supprimez simplement cette ligne.Vous pouvez vous rabattre 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 du e-sql à partir d'une collection, YMMV:
string[] ids = orders.Select(x=>x.ProductID.ToString()).ToArray(); return CurrentDataSource.Products.Where("it.ID IN {" + string.Join(",", ids) + "}");
la source
Depuis 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); }
et la requête devient:
var query2 = context.Entities.Where(BuildContainsExpression<Entity, int>(e => e.ID, ids));
la source
Je ne suis pas sûr de Silverligth, mais dans linq aux objets, j'utilise toujours any () pour ces requêtes.
var q = from t in svc.OpenTranaction where txnIds.Any(t.OpenTransactionId) select t;
la source
Pour compléter l'enregistrement, 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<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); }
la source
Voici un exemple où je montre comment écrire des requêtes basées sur des ensembles à l'aide du DataServiceContext: http://blogs.msdn.com/phaniraj/archive/2008/07/17/set-based-operations-in-ado-net-data -services.aspx
la source
Merci beaucoup. La méthode d'extension WhereIn me suffisait. Je l'ai profilé et généré la même commande SQL dans la base de données que e-sql.
public Estado[] GetSomeOtherMore(int[] values) { var result = _context.Estados.WhereIn(args => args.Id, values) ; return result.ToArray(); }
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])
la source
Je pense qu'une jointure dans LINQ peut être une solution.
Je n'ai pas testé le code cependant. J'espère que cela aide. À votre santé. :-)
List<long?> txnIds = new List<long?>(); // Fill list var q = from t in svc.OpenTransaction join tID in txtIds on t equals tID select t;
Rejoignez LINQ:
http://weblogs.asp.net/salimfayad/archive/2008/07/09/linq-to-entities-join-queries.aspx
la source
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 base de données (c'est-à-dire pas des objets en mémoire) et que vous utilisez IQueryable, il doit en fait aller à la base de données car il fait essentiellement beaucoup de conditions SQL "ou" pour vérifier la clause "where in" (exécutez-le avec SQL Profiler pour voir).
Cela peut signifier que si vous affinez un 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 était d'utiliser plusieurs jointures LINQ pour le conserver à un seul appel SQL.
la source
En plus de la réponse sélectionnée.
Remplacez
Expression.Or
parExpression.OrElse
pour utiliser par Nhibernate et corrigez l'Unable to cast object of type 'NHibernate.Hql.Ast.HqlBitwiseOr' to type 'NHibernate.Hql.Ast.HqlBooleanExpression'
exception.la source