Объем коллекций, используемых в предикатах LINQ - PullRequest
0 голосов
/ 05 августа 2010

Мне очень нравится PredicateBuilder . Это позволяет мне создавать все виды запросов очень динамично. Переменную предиката можно передавать различным объектам, и они могут добавлять к ней значения, о которых они знают, и т. Д. За исключением случаев, когда мне нужно использовать .Contains в хешированной коллекции. Bzzt! Крушение и сгорание.

Например (пример / псевдокод, это может или не может скомпилировать / запустить):

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

Когда я пытаюсь это сделать, я получаю NotSupportedException: метод 'Boolean Contains (Int32)' не имеет поддерживаемого перевода в SQL из-за того, что выбранный идентификатор HashSet не находится в области действия. Если я делаю все это одним и тем же способом, то он работает нормально.

Мне нужно знать, как правильно разрешить или скомпилировать мой предикат, чтобы он мог использоваться в другой области, отличной от той, в которой объявлен HashSet. Любая помощь?

ОБНОВЛЕНИЕ: у меня было это довольно неправильно. Приведенный ниже код работает нормально, поэтому нет конфликта контекста. Спасибо, Джей.

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

Ответы [ 2 ]

2 голосов
/ 05 августа 2010

Из исключений, которые вы цитируете, маловероятно, что объем является здесь фактором.

Мой ответ на этот вопрос заключается в том, что вам нужно объявить selectedIDs как IEnumerable<int> вместо HashSet<int> (или простоприведите его перед вызовом Contains(), но это не учитывает его работоспособность, когда все в одном методе, поэтому я не уверен.

Не увидев никакого реального кода, демонстрирующего такое поведение, он будеттрудно устранить неисправность дальше.

0 голосов
/ 05 августа 2010

Не поддавайтесь ослеплению именем Contains ... есть много названных методов, и не многие поддерживают переводы в SQL.

  • Enumerable.Contains<T> и List<T>.Contains - это методы с поддерживаемыми переводами.
  • HashSet<T>.Contains - это метод без поддерживаемых переводов.

Существует много разрешений, но я рекомендую:

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

Хотя более простое решение может быть:

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

Редактировать: Речь идет вовсе не о конкретной реализации Contains.Речь идет о том, может ли поставщик запросов linqtosql определить метод и преобразовать его в выражение SQL IN (list).Код распознавания смотрит на тип параметра , используемого в выражении.Код распознавания не использует полиморфизм / реализацию и не обходит дерево наследования в поисках других возможностей.

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

Несмотря на то, что эти параметры ссылаются на один и тот же экземпляр , происходит другое поведение перевода, поскольку тип параметра отличается.

.Contains() не вызывается при переводе, поэтому его реализация не имеет значения.Это может throw NotImplementedException или return true;

...