我真的很喜欢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()));
答案 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 NotImplementedException
或return true;