访问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。 必须有更好的设计。
非常好的问题。 以下是一些可能的解决方案,虽然他们不强制避免NRE,但他们会向调用者提供他们需要Include
东西的线索:
第一种选择是不让你的方法访问实体的非保证属性; 而是强制呼叫者通过两个实体:
public void PrintTask(Task task, User taskUser)
{
// ...
}
另一个选项是命名你的方法的参数,以便它能够告诉调用者什么是必需的:
public void PrintTask(Task taskWithUser)
{
// ...
}
您可以使用Select
扩展方法来提前加载Users
。
var task = context.Tasks.Include(x => x.Roles)
.Include(x => x.Roles.Select(r => r.User))
.FirstOrDefault();
编辑:
我可以想到几种避免NRE的方法
Include
接近实体的使用位置。 User
应该在你的循环中被延迟加载 - 但请注意,这是一个经典的选择N + 1问题,你应该使用另一个Include
来修复。
我认为问题的根源是,要么这个特殊的Role
没有一个User
,或者这个特定Role
的User
有其空集Name
。 你需要在循环中检查两个null
foreach(var r in task.Roles)
{
if (r.User != null)
Console.WriteLine(r.User.Name ?? "Name is null");
}
链接地址: http://www.djcxy.com/p/56841.html
上一篇: Avoid NullReferenceException when accessing EF Navigation Properties