Свойство навигации не оценено EF Core - PullRequest
0 голосов
/ 17 января 2019

У меня проблема в EF Core, когда я пытаюсь получить связанную сущность и все ее зависимые структуры, но не добиваюсь большого успеха с ней.

В настоящее время у меня есть такой запрос:

var user = new Guid(id);
var userCustAffs = _data.UserCustomerAffiliation.Include(x => x.Customer)
                     .ThenInclude(x => x.Brand).Where(x => x.UserId.Equals(user)).ToList();
var result =  userCustAffs.Select(p => p.Customer).ToList();

Когда я смогу сделать что-то подобное, чтобы упростить это (и удалить ненужные вещи, которые оцениваются локально по сравнению с БД)

var user = new Guid(id);
var userCustAffs = _data.UserCustomerAffiliation.Include(x => x.Customer)
                               .ThenInclude(x => x.Brand).Where(x => x.UserId.Equals(user))
                               .Select(y => y.Customer).ToList();

Однако, когда я делаю последний запрос, я получаю ошибку,

The Include operation for navigation '[x].Customer.Brand' is unnecessary and was ignored 
because the navigation is not reachable in the final query results

Тем не менее, бренд очень важен, так как он выводит некоторые свойства из модели Customer. Как правильно реструктурировать этот запрос, чтобы получить желаемые результаты (например, клиент с соответствующим брендом, ограниченный идентификатором пользователя, связанным с таблицей UserCustomerAffiliation).

Я видел рекомендацию, прежде чем «запускать» запрос от Клиента вместо UserCustomerAffiliation, но это кажется противоречащим всем моим инстинктам с точки зрения оптимизации БД (и у Клиента нет свойства навигации обратно к пользовательскому аттестату UserCustomerAffiliation) .

1 Ответ

0 голосов
/ 19 февраля 2019

Ответ на вопрос, почему это происходит, это (после некоторых исследований) довольно интересный и хороший пример того, почему для его работы важно знать, как работает EF Core.

В целом, Linq работает над идеей отложенноговыполнение.Проще говоря, если я сделаю оператор Linq для конкретной строки, он может не быть оценен или выполнен до тех пор, пока данные «не понадобятся».Большую часть времени мы сокращаем это с помощью .ToList (), который вызывает немедленное выполнение.Общая идея здесь заключается в том, что иногда наборы данных не нужны (скажем, если исключение происходит до того, как оно будет оценено, но после того, как оно будет «загружено»).

EF Core делает еще один шаг вперед и связывает идею отложенного выполнения с оптимизацией базы данных.Если, например, я получаю подмножество данных из базы данных:

var result = _context.SomeTable.Where(x => x.name == "SomeValue");

Но позже все, что меня волнует, это размер набора данных:

return result.Count;

Вызов DB можетбыть оптимизирован до

select count(*) from SomeTable where name = "SomeValue"; 

вместо

select * from SomeTable where name = "SomeValue";

Точно так же, мой запрос был оптимизирован.Поскольку я все это связал до оценки, оптимизатор EF Core выбросил нужную мне таблицу.

Причина, по которой это работает:

var user = new Guid(id);
var userCustAffs = _data.UserCustomerAffiliation.Include(x => x.Customer)
                                         .ThenInclude(x => x.Brand).Where(x => 
                                         x.UserId.Equals(user)).ToList();
var result =  userCustAffs.Select(p => p.Customer).ToList();

Это потому, что я принудительно выполняю запросэто что-то вроде

Select u.*, c.*, b.* from usercustomeraffiliation u, 
        inner join Customer c  on u.customerid = c.id 
        inner join Brand b on c.brandid = c.id 
        where u.userid = 'userId';

, а затем удаляет объект клиента (и объект бренда под ним) в памяти.Было бы эффективнее иметь возможность генерировать запрос вроде:

Select c.*, b.* from Customer c  on u.customerid = c.id 
        inner join Brand b on c.brandid = c.id 
        where c.id in (select u.customerid from usercustomeraffiliation u 
                              where u.userid = 'userId');

Но это оптимизируется.

...