C# Entity Framework:Linq 对孙子进行筛选并对父进行 Select

C# Entity Framework: Linq Filter on GrandChildren and Conduct a Select on the Parent

我们公司目前正在使用 Entity Framework Net Core 2.2 和 Sql Server

正在尝试查找购买了特定产品输入参数的所有不同客户。 当尝试执行 final select 时,它会将 b lambda 显示为 Product。我们需要最后出现的不同客户。

如何为不同的客户编写 EF Linq 查询?

var taxAgencyDistinctList = db.Customer
        .SelectMany(b => b.Transactions)
        .SelectMany(b => b.Purchases)
        .Select(b => b.Product)
        .Where(b => b.BKProduct == ProductInput) 
        .Select(b => b.).Distinct();

等价于SQL很简单:

select distinct c.customerName
from dbo.customer customer
inner join dbo.Transactions transaction
    on transaction.customerid = customer.customerid
inner join dbo.Purchases purchases
    on purchases.PurchaseId = transaction.PurchaseId
inner join dbo.Product product 
    on transaction.ProductId = product.ProductId
where tra.BKProduct = @ProductInput

公司更喜欢我们不使用 Linq 的方法 Sql,如果可能的话

资源:

var taxAgencyDistinctList = db.Purchases
        .Include(p => p.Transaction).ThenInclude(t => t.Customer)
        .Where(p => p.ProductId == ProductInput.ProductId)
        .Select(b => b.Transaction.Customer).Distinct();

你可以从另一边走。 当您执行 select 时,linq 从该 selected 类型继续。在你的情况下是产品。

第二种方法是从客户开始,然后包括在内。然后在 where close check customer purcheses.any(m => m.ProductId == input.ProductId) 或类似的东西。

如果您使用内部联接,那么这应该可以正常工作。


    var taxAgencyDistinctList = db.Customer
        .Join(db.Transactions, customer => customer.customerId, transaction => transaction.customerid, (customer, transaction) => new 
        {
        Customer = customer,
        Transaction = transaction
        })
        .Join(db.Purchases, comb => comb.Transaction.PurchaseId, purchase => purchase.PurchaseId, (comb, purchase) => new
        {
        OldCombinedObject = comb,
        Purchase = purchase
        })
        .Join(db.Product, comb => comb.OldCombinedObject.Transaction.ProductId, product => product.ProductId, (comb, product) => new
        {
        LastCombinedObject = comb,
        Product = product
        })
        .Where(comb => comb.LastCombinedObject.OldCombinedObject.Transaction.BKProduct == ProductInput) 
        .Select(comb => comb.LastCombinedObject.OldCombinedObject.Customer).Distinct();

虽然您可能通过其他一些答案获得了您想要的数据,但您可能因追求的目标而过度补水(这意味着您对数据库的访问过多)。

".Any" 是 EF 编写 "WHERE EXISTS" 子句的方式。

这是对 EF 查询的尝试:

IEnumerable<Customer> justCustomersHydrated = db.Customer
                      .Where(p => p.Transactions.SelectMany(c => c.Purchases).Select(gc => gc.Product.Where(gc => gc.BKProduct == ProductInput).Any());

我使用“p”作为父级,“c”作为子级,“gc”作为孙级。你当然可以替换那些,但我试图在代码中显示意图。

您正在尝试(生成)SQL,看起来更像这样。

select c.customerId /* and c.AllOtherColumns */
from dbo.customer customer
WHERE EXISTS
(
    SELECT 1 FROM dbo.Transactions transaction
inner join dbo.Purchases purchases
    on purchases.PurchaseId = transaction.PurchaseId
inner join dbo.Product product 
    on transaction.ProductId = product.ProductId
where tra.BKProduct = @ProductInput

AND /* relationship to outer query */
 transaction.customerid = customer.customerid
 )

这将混合客户对象(客户对象的所有标量和无导航属性)。

您可以选择 select(客户的较少标量属性).... 您也可以只 select customerid(尽管通常 selecting 来自父 table 的所有列并不是太可怕,除非 table 有 many/many 列或某处的大数据 (image/varbinary(max)) 列。

看到这个答案:

Entity Framework - check whether has grandchild records

答案中有“新{”的地方是较不激进的 SELECT。

你真的需要加入采购吗? IE 是否有一些没有购买的交易,你想排除那些有内部连接的交易?如果没有,那么

select distinct c.customerId 
from dbo.customer customer
inner join dbo.Transactions transaction
    on transaction.customerid = customer.customerid
inner join dbo.Purchases purchases
    on purchases.PurchaseId = transaction.PurchaseId
inner join dbo.Product product 
    on transaction.ProductId = product.ProductId
where tra.BKProduct = @ProductInput

就是

 var cids = db.Transactions
              .Where( t => t.Purchase.BKProduct = productImput )
              .Select(t => new
                     {
                       t.Purchase.CustomerId,
                       t.Purchase.Customer.CustomerName
                     })
              .Distinct();