2011-12-20 83 views
7

我最近修复的许多错误是在访问使用实体框架加载的对象的导航属性时导致null引用的结果。我相信我在设计我的方法方面一定存在缺陷。这里有一个例子...访问EF导航属性时避免NullReferenceException

任务包含许多角色,每个角色引用一个用户。

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

我可能已经建立了这个方法,每个意图加载角色和用户,但下次我使用它时,我可能会忘记我需要两个。理想情况下,方法定义应该告诉我什么数据是必要的,但即使我同时传递任务和角色,我仍然缺少角色 - >用户。

什么是正确的方式来引用这些关系,并确保它们是像这种打印方法加载?我对更好的设计感兴趣,所以“使用懒加载”不是我正在寻找的答案。

谢谢!

编辑:

我知道我可以加载这样的任务...

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

我想知道什么是我该怎么设计我的方法,这样,当我回来,并用它从现在起6个月,我知道需要在我的实体中加载哪些数据?方法定义并不指出使用它的必要条件。或者我如何阻止这些NullReferences。必须有更好的设计。

+0

可能重复[什么是.NET中的NullReferenceException?](http://stackoverflow.com/questions/4660142/what-is-a-nullreferenceexception-in-net) – 2011-12-20 18:31:59

+0

这与其他任何'NullReferenceException'问题没有什么不同。 – 2011-12-20 18:32:18

回答

1

很好的问题。以下是一些可能的解决方案,虽然他们不强制避免NRE,但他们会为来电者提供他们需要的线索Include东西:

第一种方法是不让您的方法访问非易失性存储器,实体的保证财产;相反,强制调用者传递两个实体:

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

另一种选择是命名方法的参数使得其将线索主叫为需要什么:的

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

谢谢。我认为你验证了我的想法,即没有纯粹的解决方案。我尝试尽可能简化财产访问,并在可能时单独传递它们。 – BZink 2011-12-21 15:28:11

2

您可以使用Select扩展方法进行急切加载Users

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

编辑:

有迹象表明,我能想到的一些方法,以避免NRE

  • 集成测试使用SQL Server CE/Express数据库。单元测试 与假上下文将无法正常工作。
  • 将实体加载到他们消耗的位置附近。因此,这些实体靠近使用实体的地方。
  • 将DTOs/ViewModels传递到上层而不通过 实体。
+0

对不起,我没有说清楚,我知道如何加载用户。事实上,你可以在一个包含声明中做到这一点,而不是两个。我正在处理的是使用哪些方法不明显需要什么引用。所以,如果我确实加载了错误的实体,并使用该方法,我会得到空引用。 – BZink 2011-12-20 02:19:09

+0

@BZink更新回答问题 – Eranga 2011-12-20 02:50:30

1

User应该延迟加载在你的循环,只是不过,请注意,这是一个经典的select N + 1问题,你应该与其他Include修复。

我认为问题的根源是,要么这个特殊的Role一个User,或者说这种特殊的RoleUser有其Name空集。你需要在你的循环中检查两者是否为空

foreach(var r in task.Roles) 
{ 
    if (r.User != null) 
     Console.WriteLine(r.User.Name ?? "Name is null"); 
}