Entity Framework 6 - загрузка данных из отношения 0: M с использованием навигационных свойств - PullRequest
0 голосов
/ 30 октября 2018

В настоящее время я использую объединения все реже и в максимально возможной степени полагаюсь на свойства навигации.

Вот очень простая схема:

Пользователи

Id              int
Surname         string
FirstName       string

Учащиеся :

Id              int
UserId          int (foreign key for Users.Id)
EnrolmentName   string
StartDate       datetime
EndDate         datetime

Пользователь может иметь 0, 1 или много связанных с ним регистраций в таблице Enrolments.

Теперь в запросе я хочу выбрать все пользовательские строки, а также столбец EnrolmentName из их первой регистрации. Мне нравится, чтобы мои запросы были максимально простыми, и выбираю только то, что мне нужно, из базы данных. Я не люблю возвращать целые объекты, если мне не нужно.

Вот мой запрос (где я выбираю соответствующие данные непосредственно в модель представления).

IList<UserVm> rows = db.Users
    .Select(
        x => new UserVm
        {
            Id = x.Id,
            Surname = x.Surname,
            FirstName = x.FirstName,
            FirstEnrolmentName = x.Enrolments.OrderBy(o => o.StartDate).FirstOrDefault().EnrolmentName
        }
    )
    .ToList();

Проблема, с которой я столкнулся, заключается в том, что она работает, но я думаю, что она потерпит неудачу, когда я столкнусь с пользователем, у которого нет регистрации. Я ожидаю, что следующая строка будет жаловаться, что столбец EnrolmentName не может быть найден на нулевом объекте.

FirstEnrolmentName = x.Enrolments.OrderBy(o => o.StartDate).FirstOrDefault().EnrolmentName

На самом деле происходит то, что столбец EnrolmentName остается пустым, если для этого пользователя нет записей Enrolment.

Я хотел бы знать:

  1. Почему этот запрос работает и не вызывает ошибку у ученика с 0 Enrolments.

  2. Существует ли более чистый способ написания запроса, чтобы он по-прежнему обращался к базе данных только на 1 единицу и выбирал только необходимое подмножество столбцов, а не все из них.

1 Ответ

0 голосов
/ 30 октября 2018

Вы правы, вам следует выбирать только те свойства, которые вы фактически планируете использовать, таким образом, передавая как можно меньше данных в локальный процесс.

LINQ знает два вида операторов: те, которые составляют запрос, и те, которые будут выполнять запрос. Композиторы - это методы LINQ, которые возвращают IQueryable<...> (или IEnumerable<...>), исполнители - это функции, которые не возвращают IQueryable, а TResult. Примеры: ToList, FirstOrDefault, Any, Max, ...

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

db.Users.Select(user => new UserVm
{
    ...
    FirstEnrolMentName = user.Enrolments
        .OrderBy(enrolment => enrolment.StartDate)
        .FirstOrDefault()
        .EnrolmentName,
});

Интересно, будет ли это работать, если у вас есть пользователь без Enrolments. Мое первое предположение было бы, что FirstOrDefault вернет ноль, и, таким образом, у вас будет ArgumentNullException, когда вы хотите получить EnrolmentName

Другая проблема заключается в том, что вы сначала выбираете одно полное Enrolment, после чего отбрасываете все свойства регистрации, кроме имени. Было бы лучше выбрать только те свойства, которые вы планируете использовать, а затем использовать FirstOrDefault:

var result = db.Users.Select(user => new UserVm
{
    ...
    FirstEnrolMentName = user.Enrolments
        .OrderBy(enrolment => enrolment.StartDate)
        .Select(enrolment => enrolment.EnrolmentName)
        .FirstOrDefault(),
});
...