Question

J'aime vraiment PredicateBuilder . Il me permet de construire très dynamiquement toutes sortes de requêtes. La variable sous-jacente peut être passé autour des objets différents et ils peuvent ajouter sur avec les valeurs qu'ils connaissent, etc. Sauf quand je suis besoin d'utiliser un .Contains sur une collection hachée. Bzzt! Crash and Burn.

Par exemple (exemple / pseudo-code, cela peut ou peut ne pas compiler / exécuter):

protected Expression<Func<MyClass, bool>> GetWherePredicate()
{
    string[] selectedValues = Request.Form.GetValues("checkbox1") ?? new string[0];
    HashSet<int> selectedIDs = new HashSet<int>(selectedValues.Cast<int>());

    Expression<Func<MyClass, bool>> predicate = PredicateBuilder.True<MyClass>();
    predicate = predicate.And(s => selectedIDs.Contains(s.ID));

    return predicate;
}

protected void Retrieve()
{
    Expression<Func<MyClass, bool>> predicate = GetWherePredicate();
    IEnumerable<MyClass> retrievedValues = MyDataContext.GetTable<MyClass>.Where(predicate);
}

Lorsque je tente de le faire, je reçois un NotSupportedException: Méthode « booléenne contains (Int32) » n'a pas de traduction pris en charge à SQL en raison de la selectedIDs HashSet ne pas être portée. Si je fais tout dans la même méthode, il fonctionne très bien.

Je dois connaître la bonne façon d'obtenir mon prédicat là pour résoudre ou compiler ou tout afin qu'il puisse être utilisé dans un cadre différent de celui où l'HashSet est déclarée. Toute aide?

Mise à jour: J'ai eu ce joli mal. Le code ci-dessous fonctionne très bien, donc il n'y a pas de conflit de portée. Merci Jay.

string[] selectedValues = Request.Form.GetValues("checkbox1") ?? new string[0];
Expression<Func<MyClass, bool>> predicate = PredicateBuilder.True<MyClass>();
predicate = predicate.And(s => selectedValues.Contains(s.ID.ToString()));
Était-ce utile?

La solution

A partir de l'exception que vous citez, il semble peu probable que la portée est un facteur ici.

Ma réponse à cela est que vous devez déclarer selectedIDs comme IEnumerable<int> au lieu de HashSet<int> (ou tout simplement le jeter avant d'appeler Contains(), mais cela ne tient pas compte fonctionner lorsque tous dans la même méthode, donc je ne suis pas sûr.

Sans voir un code réel qui expose ce comportement, il sera difficile de résoudre plus loin.

Autres conseils

Ne vous laissez pas éblouir par-Razzle le nom Contains ... il existe de nombreuses méthodes qui sont nommées, et pas beaucoup ont pris en charge des traductions dans SQL.

  • Enumerable.Contains<T> et List<T>.Contains sont des méthodes avec des traductions prises en charge.
  • HashSet<T>.Contains est une méthode qui n'a pas de traduction pris en charge.

Il y a beaucoup de résolutions, mais je recommande:

IEnumerable<string> selectedValueQuery =
  Request.Form.GetValues("checkbox1") ?? Enumerable.Empty<string>();
List<string> selectedIds = selectedValueQuery
  .Cast<int>()
  .Distinct()
  .ToList();

Bien qu'une solution plus simple pourrait être:

IEnumerable<int> selectedIDs = new HashSet<int>(selectedValues.Cast<int>()); 

Edit: Il est pas mise en œuvre concrète Contient du tout. Il est de savoir si le fournisseur de requête LinqToSql peut identifier la méthode et la traduire en une expression sql (liste). Le code de reconnaissance regarde le type du paramètre utilisé dans l'expression. Le code de reconnaissance n'utilise pas polymorphisme / mise en œuvre ni marcher l'arbre inheritence la recherche d'autres possibilités.

List<int> myList = new List<int>(){1, 2, 3};
IList<int> myIList = myList;
IEnumerable<int> myIEnumerable = myList;

  //works by List<T>.Contains()
db.Customers.Where(c => myList.Contains(c.CustomerID));

  //doesn't work, no translation for IList<T>.Contains
db.Customers.Where(c => myIList.Contains(c.CustomerID));

  //works by Enumerable.Contains<T>()
db.Customers.Where(c => myIEnumerable.Contains(c.CustomerID));

  //works by Enumerable.Contains<T>()
db.Customers.Where(c => Enumerable.Contains(myIEnumerable, c.CustomerID));

Même si ces paramètres font référence au même instance , différents comportements de traduction se produisent parce que le type du paramètre est différent.

.Contains() n'est pas appelé comme il est traduit, par conséquent sa mise en œuvre est hors de propos. Il pourrait throw NotImplementedException ou return true;

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top