代码之家  ›  专栏  ›  技术社区  ›  ChrisN

LINQ谓词中使用的集合范围

  •  0
  • ChrisN  · 技术社区  · 14 年前

    我真的很喜欢 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 因为selectedids哈希集不在作用域中。如果我用同样的方法来做这一切,那么它会很好地工作。

    我需要知道正确的方法,让我的谓词在那里解析或编译,或者其他什么,这样它就可以在声明哈希集的不同范围内使用。有什么帮助吗?

    更新:我完全错了。下面的代码工作正常,因此没有范围冲突。谢谢杰伊。

    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 回复  |  直到 14 年前
        1
  •  2
  •   Jay    14 年前

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

    我的答案是你需要申报 selectedIDs 作为 IEnumerable<int> 而不是 HashSet<int> (或者在打电话之前先把它扔出去 Contains() 但这并不能解释它在同样的方法下工作的原因,所以我不确定。

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

        2
  •  0
  •   Amy B    14 年前

    不要被这个名字弄得眼花缭乱 Contains …有许多方法被命名为that,但很少有方法支持转换为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(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;