Избегайте исключения NullReferenceException при доступе к свойствам навигации EF - PullRequest
7 голосов
/ 20 декабря 2011

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

Задача содержит много ролей, каждая роль ссылается на пользователя.

public class Role
{
    public int Id;
    public int User_Id;
    public string Type;
}

public class User
{
    public int Id
    public string Name;
}    

public class Task
{
    public int Id;
    public string Name;
    public string Status;
    public List<Role> Roles;
}

Учитывая, что я бы по ошибке запросил свой контекст и не загрузил бы пользователя ...

var task = context.Tasks.Include(x=>x.Roles).FirstOrDefault;

И затем я вызываю этот метод...

public void PrintTask(Task task)
{
    Console.WriteLine(task.Name);
    Console.WriteLine(task.Status);

    foreach(var r in task.Roles)
    {
        Console.WriteLine(r.User.Name); //This will throw NRE because User wasn't loaded
    }
}

Возможно, я построил этот метод с каждым намерением загрузить роли и пользователя, но в следующий раз, когда я его использую, я могу забыть, что мне нужны оба.В идеале определение метода должно сказать мне, какие данные необходимы, но даже если я передам и Task, и Roles, мне все еще не хватает Roles-> User.

Как правильно ссылаться на эти отношения и быть уверенным, что они загружены примерно таким же способом печати?Я заинтересован в лучшем дизайне, поэтому «Использовать Lazy Loading» - это не тот ответ, который я ищу.

Спасибо!

РЕДАКТИРОВАТЬ:

Я знаю, что могу загрузить задачу следующим образом ...

var task = context.Tasks.Include(x=>x.Roles.Select(z=>z.User)).FirstOrDefault();

То, что я хочу знать, какя должен разработать свой метод так, чтобы, когда я вернусь и использую его через 6 месяцев, я знал, какие данные должны быть загружены в мои объекты?Определение метода не указывает, что необходимо для его использования.Или как мне заблокировать эти NullReferences.Должен быть лучший дизайн.

Ответы [ 3 ]

2 голосов
/ 20 декабря 2011

Вы можете использовать метод расширения Select для загрузки Users.

var task = context.Tasks.Include(x => x.Roles)
             .Include(x => x.Roles.Select(r => r.User))
             .FirstOrDefault();

Edit:

Есть несколько способов избежать NRE

.
  • Интеграционный тест с использованием базы данных SQL Server CE / Express. Модульное тестирование с поддельным контекстом не будет работать правильно.
  • Загрузка объектов близко к месту их потребления. Таким образом Include s близко к тому месту, где используются объекты.
  • Передача DTO / ViewModels на верхние уровни без передачи лиц.
1 голос
/ 20 декабря 2011

Очень хороший вопрос. Вот некоторые возможные решения, которые, хотя они и не навязывают предотвращение NRE, они дадут подсказке вызывающей стороне, что им нужно для Include вещей:

Первый вариант - не давать вашему методу доступ к негарантированному свойству объекта; вместо этого заставьте абонента передать обе сущности:

public void PrintTask(Task task, User taskUser)
{
    // ...
}

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

public void PrintTask(Task taskWithUser)
{
    // ...
}
1 голос
/ 20 декабря 2011

User должен быть лениво загружен в ваш цикл - просто обратите внимание, что это классическая select N + 1 проблема, которую вы должны решить с помощью другой Include.

Я думаю, что основная проблема в том, что данный конкретный Role не имеет a User, или что этот конкретный Role 'User имеет нулевое значение для его Name. Вам нужно будет проверить оба на нуль в вашем цикле

foreach(var r in task.Roles)
{
    if (r.User != null)
        Console.WriteLine(r.User.Name ?? "Name is null"); 
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...