EF查找不在列表

时间:2016-03-08 05:54:01

标签: asp.net-mvc entity-framework linq ef-code-first linq-to-entities

我有这样的实体:

产品

public class Product 
{
    public int Id { get; set; }
    public string Name { get; set; }
}

价目表

public class PriceList
{
    public int Id { get; set; }
    public string Name { get;set; }
}

PriceListProduct

public class PriceListProduct 
{
    public int Id { get; set; }
    public int PriceListId { get; set; }
    public int ProductId { get; set; }

    public virtual Product Product { get; set; }
}

问题是,如何使用LINQ获取不在价目表中的产品?

我的第一个想法是使用Contains,但产品列表可能大于100000,如果Contains翻译为像WHERE NOT IN子句这样的查询,SQL有大约2000个参数的限制,所以除了性能,我认为这不是最好的方法。

还有其他方法吗?我应该使用原始查询吗?

更新#1

我试图在@Indregaard回答之后了解GroupJoin。到目前为止,我有这个。

var productsWithNoPrice = db.Product()
                .GroupJoin(db.PriceListProduct().Where(plp => plp.PriceListId == 2)
                .Select(plp => plp.Product),
                p => p.Id,
                plp => plp.Id,
                (p, product) => new { p.Id, Product = product })
                .Where(p => !p.Product.Any())
                .Select(p => p.Product);

使用过滤器

.Where(plp => plp.PriceListId == 2)

我正在使用ID 2过滤价格表中的产品。我认为这很接近,但SQL生成的查询会返回一些行,这些行对应于价目表中不存在的产品数量但每一行列为空。

基本上我需要的是像这样的查询

select * from Product p
left join PriceListProduct plp on plp.ProductId = p.Id and plp.PriceListId = 2
where plp.Id is null

2 个答案:

答案 0 :(得分:3)

所以你正在寻找Antijoin

手动方法可能是这样的:

var query = 
    from p in db.Products
    join plp in db.PriceListProducts 
    on p.Id equals plp.ProductId into priceLists
    where !priceLists.Any()
    select p;

另一种方式:

var query = db.Products
    .Where(p => !db.PriceListProducts.Any(plp => p.Id == plp.ProductId));

但最好的方法是在模型中创建所有导航属性

public class Product 
{
    public int Id { get; set; }
    public string Name { get; set; }
    public ICollection<PriceListProduct> PriceLists { get; set; }
}

让EF为您创建查询

var query = db.Products.Where(p => !p.PriceLists.Any());

答案 1 :(得分:0)

你试过Join/GroupJoin吗? 我没有尝试过反对数据库来查看生成的sql是否有效/工作但是对于常规对象这样的东西是可行的。

var productsWithNoPrices = products.GroupJoin(productPriceList,
            product => product.Id,
            productprice => productprice.ProductId,
            (product, productPrice) => new { Product = product, Prices = productPrice})
            .Where(c=>!c.Prices.Any()).Select(c=>c.Product);

修改:根据您更新的问题,我认为您需要这样的内容:

var productsWithNoPrices = db.Products.GroupJoin(db.PriceListProducts.Where(c => c.PriceListId == 2),
                product => product.Id,
                productprice => productprice.ProductId,
                (product, productPrice) => new { Product = product, Prices = productPrice }).Where(c=>!c.Prices.Any()).Select(c=>c.Product);

GroupJoin将在你的左表(db.Products)中进行更新,在右表中加入什么(db.PriceListProducts.xxxxx)(参数1):左表中的每个产品都将从右边获得匹配列表,结合产品ID(参数2和3),输出到匿名类型(参数4)。在没有产品价格的地方过滤所有这些并选择产品。这导致以下SQL,它似乎给出了期望的结果?

SELECT 
[Extent1].[Id] AS [Id], 
[Extent1].[Name] AS [Name]
FROM [dbo].[Products] AS [Extent1]
WHERE  NOT EXISTS (SELECT 
    1 AS [C1]
    FROM [dbo].[PriceListProducts] AS [Extent2]
    WHERE (2 = [Extent2].[PriceListId]) AND ([Extent1].[Id] = [Extent2].[ProductId])
)