Как выбрать отдельные элементы из таблицы по переменной, а затем присоединиться к другой таблице в LINQ

У меня есть запрос, в котором мне нужно выбрать отдельные номера заказов из таблицы заказов. Номер заказа может соответствовать нескольким записям в таблице заказов, но мне нужны только отдельные заказы. И.Е. Я хочу получить только одну запись для данного номера заказа. Затем я хочу объединить это с таблицей клиентов на o.CustomerKey, равной c.CustomerKey. Я пробовал сделать это несколькими разными способами и не нашел быстрого и краткого способа сделать это. Моя мысль состоит в том, чтобы выбрать отдельные заказы по номеру заказа, затем присоединиться к таблице «Клиенты», где соблюдаются условные критерии, а затем выбрать данные, которые мне нужно вернуть. Это с Entity Framework Core 8.0.6.

CustomerOrder — это DTO с полями, которые можно увидеть в запросе.

Это соответствующие столбцы таблицы «Клиент»: [Ключ_Клиента],[Номер_Клиента],[Фамилия_Имя],[Имя_Имя],[Адрес],[Город],[Штат],[Почтовый_код],[электронная почта],[Телефон].

Это соответствующие столбцы заказа: [Номер_заказа],[Ключ_клиента]

Эти таблицы были созданы для создания сущностей в моем коде.

Это один запрос, который я пробовал:

public async Task<List<CustomerOrder>> GetOrdersMatchingAsync(string lastName, string address, string city, string state, string zipCode)
        {
            var query = 
            from c in DimensionCustomers
            join o in (
                from o in FactDirectOrders
                select new { o.OrderNumber, o.CustomerKey }
            ).Distinct() on c.CustomerKey equals o.CustomerKey
            where
            (string.IsNullOrEmpty(lastName) || c.LastName == 
            lastName) &&
            (string.IsNullOrEmpty(address) || c.Address == address) 
             &&
            (string.IsNullOrEmpty(zipCode) || c.ZipCode == zipCode) 
             &&
            (string.IsNullOrEmpty(city) || c.City == city) &&
            (string.IsNullOrEmpty(state) || c.State == state)
            select new CustomerOrder
            {
                OrderNumber = o.OrderNumber,
                CustomerNo = c.CustomerNo,
                CustomerKey = c.CustomerKey,
                FirstName = c.FirstName,
                LastName = c.LastName,
                Address = c.Address,
                City = c.City,
                State = c.State,
                ZipCode = c.ZipCode,
            };
            return await query.AsNoTracking().ToListAsync();
        }

В настоящее время это работает, но я не уверен, что это оптимально. У меня есть пара подобных запросов, в которых я хочу выбрать отдельные значения перед объединением, но, возможно, это не лучшая практика? Любые рекомендации или советы будут оценены по достоинству.

Что мы здесь видим? Что такое Orders, что такое Customers? Вроде бы это EF, но какая версия? И как выглядят занятия, особенно? их навигационные свойства? Пожалуйста, отредактируйте вопрос и теги, чтобы добавить дополнительную информацию.

Gert Arnold 20.06.2024 17:28

@GertArnold отредактировал дополнительную информацию, дайте мне знать, если есть что-нибудь еще, что можно было бы добавить.

Turner 20.06.2024 19:44
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
56
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я вижу несколько проблем с вашим запросом. Сначала очевидные вещи:

Это можно значительно упростить, используя свойства навигации для связей между сущностями. EF — это не просто замена ADO с поддержкой Linq и эффективное создание операторов SQL.

При проектировании (с использованием Select) использования DTO, ViewModels или заполнения «новых» экземпляров сущностей вам не нужен AsNoTracking(), DTO CustomerOrder — это не что-то, что будет отслеживать DbContext, а также сущность/таблица, на которую ссылается запрос.

При работе с критериями условного поиска лучше извлечь их из выражения Linq и применять в Linq только в том случае, если они необходимы, а не встраивать в Linq такие вещи, как проверки #null. То, как они у вас есть, приведет к множеству сравнений #null в SQL.

Используя свойства навигации, я ожидаю, что сущность «Клиент» должна иметь коллекцию заказов или, по крайней мере, «Заказ» имеет свойство навигации обратно к своему «Клиенту». Чтобы получить запрос с использованием свойств навигации и извлечения условных проверок #null, ваш запрос можно упростить до чего-то вроде:

var query = _context.DirectOrders.AsQueryable();

// Each filter will only be appended (ANDed together) if the value is present.
if (!string.IsNullOrEmpty(lastName))
    query = query.Where(o => o.Customer.LastName == lastName);

if (!string.IsNullOrEmpty(address)) 
    query = query.Where(o => o.Customer.Address == address); 

if (!string.IsNullOrEmpty(zipCode))
    query= query.Where( o => o.Customer.ZipCode == zipCode);
             
if (!string.IsNullOrEmpty(city) 
    query = query.Where*o => o.Customer.City == city));

if (!string.IsNullOrEmpty(state))
    query = query.Where(o => o.Customer.State == state)

var results = await query.Select(o => new CustomerOrder 
{
    OrderNumber = o.OrderNumber,
    CustomerNo = o.Customer.CustomerNo,
    CustomerKey = o.Customer.CustomerKey,
    FirstName = o.Customer.FirstName,
    LastName = o.Customer.LastName,
    Address = o.Customer.Address,
    City = o.Customer.City,
    State = o.Customer.State,
    ZipCode = o.Customer.ZipCode,
}).Distinct().ToListAsync();

return results;

Использование Distinct() предполагает, что ваши DirectOrders могут иметь несколько строк для одного и того же OrderNumber. Свойства навигации между сущностями могут быть двунаправленными (Заказ имеет ссылку на Клиента, И Клиент имеет коллекцию Заказов) или однонаправленными. (Заказ имеет ссылку на клиента ИЛИ у клиента есть коллекция заказов). Обычно я рекомендую использовать свойства однонаправленной навигации, где одна сущность может служить совокупным корнем для другой. Однако в некоторых случаях, когда две сущности обычно работают как сущности верхнего уровня (например, «Клиент» и «Заказ»), имеет смысл использовать двунаправленные ссылки. Например, если у вас есть только Customer.Orders и нет Order.Customers, запрос немного изменится:

var query = _context.Customers.AsQueryable();

// Each filter will only be appended (ANDed together) if the value is present.
if (!string.IsNullOrEmpty(lastName))
    query = query.Where(c => c.LastName == lastName);

if (!string.IsNullOrEmpty(address)) 
    query = query.Where(c => c.Address == address); 

if (!string.IsNullOrEmpty(zipCode))
    query= query.Where( c => c.ZipCode == zipCode);
             
if (!string.IsNullOrEmpty(city) 
    query = query.Where*c => c.City == city));

if (!string.IsNullOrEmpty(state))
    query = query.Where(c => c.State == state)

var results = await query.SelectMany(c => c.Orders.Select( o => new CustomerOrder 
{
    OrderNumber = o.OrderNumber,
    CustomerNo = c.CustomerNo,
    CustomerKey = c.CustomerKey,
    FirstName = c.FirstName,
    LastName = c.LastName,
    Address = c.Address,
    City = c.City,
    State = c.State,
    ZipCode = c.ZipCode,
})).Distinct().ToListAsync();

return results;

При этом выполняется запрос с уровня клиента, а затем SelectMany(), чтобы перейти к заказам, создавая DTO из объединенного клиента (c=>) и заказа (o=>).

Я ценю ответ, я думаю, что здесь у меня есть хорошие возможности для обучения. В дальнейшем, помогут ли эти запросы решить мою проблему? И.Е. использование Distinct() будет ли это также отличать строки по CustomerNo? Мы могли бы упростить пример, чтобы он относился не только к моему коду. По сути, я хочу выбрать первый экземпляр каждого номера заказа, в котором совпадают данные Клиента. Это означает, что результат может содержать несколько одинаковых клиентов для разных заказов. У заказов нет Customer, только CustomerKey и CustomerNo в заказе.

Turner 21.06.2024 15:46
Distinct почти наверняка не требуется, если настроены свойства навигации. Если вам нужен список клиентов, соответствующих предоставленным критериям, с каждым из соответствующих номеров заказов, а ваша сущность «Клиент» настроена с коллекцией «Заказы», ​​тогда вам просто нужно использовать второй пример выше и удалить Distinct(), как это красиво. многое в этом случае ничего не даст.
Steve Py 23.06.2024 23:49

Ах, я понимаю, SelectMany() в сочетании с Distinct() выбирает все отдельные заказы для данного клиента. Спасибо!

Turner 24.06.2024 15:57

Другие вопросы по теме