LINQ谓词中使用的集合范围

时间:2010-08-05 03:26:39

标签: c# linq-to-sql predicatebuilder

我真的很喜欢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:方法'布尔包含(Int32)'没有支持转换为SQL ,因为selectedIDs HashSet不在范围内。如果我以相同的方法完成所有操作,那么它可以正常工作。

我需要知道正确的方法来获取我的谓词来解析或编译或其他什么,以便它可以在声明HashSet的不同范围内使用。有什么帮助吗?

更新:我有这个错误。下面的代码工作正常,因此没有范围冲突。谢谢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()));

2 个答案:

答案 0 :(得分:2)

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

我对此的回答是,您需要将selectedIDs声明为IEnumerable<int>而不是HashSet<int>(或者只是在调用Contains()之前将其强制转换,但这不是帐号因为它以相同的方法工作,所以我不确定。

如果没有看到任何显示此行为的实际代码,将很难进一步排除故障。

答案 1 :(得分: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>()); 

编辑:这根本不是关于包含的具体实现。它是关于linqtosql查询提供程序是否可以识别该方法并将其转换为IN(list)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));

即使这些参数引用相同实例,也会出现不同的翻译行为,因为参数的类型不同。

.Contains()在被翻译时未被调用,因此其实现无关紧要。它可以throw NotImplementedExceptionreturn true;