C# Entity Framework: Фильтр Linq для GrandChildren и выполнение выбора на родительском элементе - PullRequest
1 голос
/ 20 июня 2020

Наша компания в настоящее время использует Entity Framework Net Core 2.2 с Sql Сервером

Пытаясь найти всех клиентов Distinct, которые приобрели определенный входной параметр продукта. При попытке сделать окончательный выбор он показывает лямбда b как Продукт. Нам нужно, чтобы отдельные клиенты появлялись последними.

Как написать запрос 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, если возможно

Ресурсы:

Фильтрация при включении в EF Core

Фильтрация на ThenInclude три вложенных уровня вниз

Net Core: Entity Framework ThenInclude with Projection Выбрать

Ответы [ 4 ]

3 голосов
/ 20 июня 2020

Хотя вы можете получить нужные данные с некоторыми другими ответами, вы, вероятно, чрезмерно увлажняете (что означает, что вы слишком сильно попадаете в БД) для того, что вам нужно.

".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» как Parent, «c "как Child и" g c "как GrandChild. Вы, конечно, можете заменить их, но я пытаюсь показать намерение в коде.

Вы пытаетесь получить (сгенерированный) 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
 )

Это приведет к гидратации объекта Customer (все скаляры и отсутствие свойств навигации объекта Customer).

По желанию вы можете выбрать (меньше скалярных свойств Customer) .... Вы также можете просто выбрать Customerid (хотя обычно выбор всех столбцов из родительской таблицы не так уж и ужасен, если только в этой таблице не много / много столбцов или где-то есть столбец с большими данными (image / varbinary (max)).

См. это ответ:

Entity Framework - проверьте, есть ли записи о внуках

Где в этом ответе есть "new {" для менее агрессивного SELECT.

1 голос
/ 20 июня 2020

Вам действительно нужно присоединиться к Покупкам. 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();
1 голос
/ 20 июня 2020

Если вы go с внутренними соединениями, тогда это должно работать нормально.


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

0 голосов
/ 20 июня 2020
var taxAgencyDistinctList = db.Purchases
        .Include(p => p.Transaction).ThenInclude(t => t.Customer)
        .Where(p => p.ProductId == ProductInput.ProductId)
        .Select(b => b.Transaction.Customer).Distinct();

Можно go с другой стороны. Когда вы делаете выбор, linq продолжает работу с этого выбранного типа. В вашем случае это product.

Второй подход будет начинаться с Customer и go включая. Затем, где внимательно проверьте клиента purcheses.any (m => m.ProductId == input.ProductId) или что-то в этом роде.

...