非常复杂的linq

时间:2010-12-14 17:46:37

标签: linq linq-to-sql linq-to-entities linq-to-objects

我无法将复杂的sql查询重写为linq。

-- gets the SpecificationAttributeOptionIDs that are in the FilteredSpecs list, but don't match
    SELECT * FROM #FilteredSpecs [fs] 
                            WHERE [fs].SpecificationAttributeOptionID NOT IN 
                            ( 
                                -- gets the SpecificationAttributeOptionIDs that match
                                SELECT psam.SpecificationAttributeOptionID 
                                FROM dbo.Nop_Product_SpecificationAttribute_Mapping psam 
                                WHERE psam.AllowFiltering = 1 AND psam.ProductID = 1 
                            ) 

基本上我所拥有的是一个列表FilteredSpec,它是C#的一个列表。我正在尝试从FilteredSpec列表集中获取所有具有所有属性选项的产品。

我试过的是这个(因为我对linq的了解有限,但显然不起作用):

var query = from p in Products
                        where (p.NpProductSpecificationAttributes.Select(a => filters.Contains(a.NpSpecificationAttributeOption.SpecificationAttributeOptionId)).Count() == 0)
                        select p;

有人可以指导我朝正确的方向发展吗?

2 个答案:

答案 0 :(得分:4)

首先,永远不要使用Count(),除非你真的需要知道究竟有多少元素。使用Count()测试空集可能需要遍历整个数据集。当数据集大小超过少数几个项目时,这对性能至关重要。使用 ! .Any()而不是.Count()== 0,.Any()而不是.Count()> 0

接下来,原始SQL中的NOT IN测试听起来像是设置差异操作。在LINQ中,由.Except()表示A.除外(B)返回A中未找到的所有A元素。

我对你的问题描述感到有些困惑。在文本中,您说您要查找具有在FilterSpec列表中找到的所有属性选项的所有产品。但是在你的两个代码示例中,你正在使用NOT IN或者测试Contains返回一个空结果,它似乎是在文本描述的相反方向上运行。

如果您要查找具有FilterSpec列表中找到的所有属性选项的所有产品,那么您正在寻找设置等效。

如果您的attributesoptions和filterspec列表中的项目始终按特定顺序列出,那么您可以使用Linq的.SequenceEqual()函数。我假设你的属性选项没有被排序,所以.SequenceEqual()不是正确的解决方案。

要测试两组值A和B的等价性而不依赖于顺序,可以测试A.除非(B)为空且B.Except(A)为空。使用 ! .Any()测试为空。第一个说A中的所有东西都可以在B中找到,第二个说B中的所有东西都可以在A中找到。还有什么?没什么,所以两组必须包含完全相同的元素。

尝试这样的事情(未经测试):

var query = from p in Products
            where !p.NpProductSpecificationAttributes.Except(filters).Any() &&
                  !filters.Except(p.NpProductSpecificationAttributes).Any()
            select p;

如果只是为了减少上面表达式必须在内部创建哈希集的次数,那么使用HashSet可能会获得更好的性能。下面的代码假定过滤器列表和产品属性中没有重复的项目。如果您的SpecificationAttributeOptionId不是基本类型(string,int),您可能还需要指定相等比较器。

    var filterset = new HashSet<filteritemtype>(filters);
    var query = from p in Products
                where filterset.SetEquals(new HashSet<itemtype>(p.NpProductSpecificationAttributes))
                select p;

答案 1 :(得分:2)

这可能是最简单的:

var query = Products.Where(
    p => FilteredSpec.All(
       fs => p.NpProductSpecificationAttributes.Any(
          a => a.NpSpecificationAttributeOption.AllowFiltering &&
               a.NpSpecificationAttributeOption.SpecificationAttributeOptionId == fs)));

如果FilteredSpec实际上是List对象,LINQ to Entities可能不知道如何翻译它。如果是这种情况,请告诉我们,我们会看看是否可以提出其他解决方案。