LINQ 谓词中使用的集合范围

发布于 2024-09-12 18:28:22 字数 1472 浏览 4 评论 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:方法'Boolean Contains(Int32)'没有支持对 SQL 的翻译 由于 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()));

I really like PredicateBuilder. It allows me to build all sorts of queries very dynamically. The predicate variable can be passed around to different objects and they can add onto it with values they know about, etc. Except when I am needing to use a .Contains on a hashed collection. Bzzt! Crash and burn.

For instance (example/pseudo code, this may or may not compile/run):

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

When I try to do that, I get a NotSupportedException: Method 'Boolean Contains(Int32)' has no supported translation to SQL due to the selectedIDs HashSet not being in scope. If I do this all in the same method, then it works fine.

I need to know the right way to get my predicate there to resolve or compile or whatever so that it can be used in a different scope from where the HashSet is declared. Any help?

UPDATE: I had this pretty wrong. The code below works fine, so there is no scope conflict. Thanks 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()));

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

等往事风中吹 2024-09-19 18:28:23

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

我对此的回答是,您需要将 selectedIDs 声明为 IEnumerable 而不是 HashSet (或者在调用之前将其强制转换) Contains(),但这并不能说明它在同一方法中工作,所以我不确定

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

From the exception you cite, it seems unlikely that scope is a factor here.

My answer to this is that you need to declare selectedIDs as IEnumerable<int> instead of HashSet<int> (or just cast it before calling Contains(), but that doesn't account for it working when all in the same method, so I'm unsure.

Without seeing any actual code that is exhibiting this behaviour, it will be difficult to troubleshoot any further.

若相惜即相离 2024-09-19 18:28:23

不要被 Contains 这个名字弄得眼花缭乱……有很多方法都是这样命名的,但支持 SQL 转换的方法并不多。

  • Enumerable.ContainsList.Contains 是支持翻译的方法。
  • HashSet.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));

即使这些参数引用同一实例,由于参数的类型不同,也会出现不同的转换行为。

.Contains() 在翻译时不会被调用,因此它的实现是无关紧要的。它可以抛出NotImplementedException返回true;

Do not get razzle-dazzled by the name Contains... there are many methods that are named that, and not many have supported translations into SQL.

  • Enumerable.Contains<T> and List<T>.Contains are methods with supported translations.
  • HashSet<T>.Contains is a method that has no supported translation.

There are many resolutions, but I recommend:

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

Although a simpler solution might be:

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

Edit: It's not about concrete implementation of Contains at all. It's about whether the linqtosql query provider can identify the method and translate it into an IN (list) sql expression. The recognition code looks at the type of the parameter used in the expression. The recognition code does not use polymorphism/implementation nor does it walk the inheritence tree looking for other possibilities.

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

Even though these parameters reference the same instance, different translation behaviors occur because the type of the parameter is different.

.Contains() is not called as it is translated, therefore its implementation is irrelevant. It could throw NotImplementedException or return true;

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文