2011-12-20 2 views
7

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

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

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 
    } 
} 

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

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

Спасибо!

EDIT:

Я знаю, что я могу загрузить задачу, как это ...

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

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

+0

возможно дубликат [ Что такое исключение NullReferenceException в .NET?] (Http://stackoverflow.com/questions/4660142/what-is-a-nullreferenceexception-in-net) –

+0

Это ничем не отличается от любой другой проблемы «NullReferenceException». –

ответ

1

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

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

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

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

public void PrintTask(Task taskWithUser) 
{ 
    // ... 
} 
+0

Спасибо. Я думаю, вы подтвердили мои мысли о том, что для этого нет чистого решения. Я стараюсь максимально упростить доступ к ресурсам и передавать их индивидуально, когда это возможно. – BZink

2

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

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

Edit:

Есть несколько способов, которые я могу думать о том, чтобы избежать ЯРД

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

Извините, я не дал понять, что я знал, как загрузить пользователя. И на самом деле вы можете сделать это в одном утверждении Include, а не в двух. Я имею дело с методами, в которых неясно, какие ссылки необходимы. Поэтому, если я загружаю объект неправильно и использую этот метод, я получаю нулевую ссылку. – BZink

+0

@BZink обновленный ответ – Eranga

1

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

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

foreach(var r in task.Roles) 
{ 
    if (r.User != null) 
     Console.WriteLine(r.User.Name ?? "Name is null"); 
} 
Смежные вопросы