0

我真的很喜欢PredicateBuilder。它允许我非常动态地构建各种查询。谓词变量可以传递给不同的对象,并且他们可以添加他们知道的值等。除非我需要在散列集合上使用 .Contains 。呸!崩溃和燃烧。

例如(示例/伪代码,这可能会或可能不会编译/运行):

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: Method 'Boolean Contains(Int32)' has no supported translation to SQL due to selectedIDs 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()));
4

2 回答 2

2

从您引用的例外情况来看,范围似乎不太可能是这里的一个因素。

我对此的回答是,您需要声明selectedIDsIEnumerable<int>而不是HashSet<int>(或者只是在调用之前将其转换Contains()

如果没有看到任何表现出这种行为的实际代码,就很难进一步排除故障。

于 2010-08-05T03:46:35.743 回答
0

不要被这个名字弄得眼花缭乱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 查询提供程序是否可以识别该方法并将其转换为 IN(列表)sql 表达式。识别代码查看表达式中使用的参数的类型。识别代码不使用多态性/实现,也不遍历继承树寻找其他可能性。

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

即使这些参数引用同一个 instance ,由于参数的类型不同,也会出现不同的翻译行为。

.Contains()在翻译时没有调用,因此它的实现是无关紧要的。它可以throw NotImplementedExceptionreturn true;

于 2010-08-05T17:47:24.053 回答