简体   繁体   English

访问EF导航属性时,请避免NullReferenceException

[英]Avoid NullReferenceException when accessing EF Navigation Properties

Many of the bugs I've been fixing lately are a result of null references when accessing navigation properties of objects loaded using entity framework. 在访问使用实体框架加载的对象的导航属性时,我最近修复的许多错误都是空引用的结果。 I believe there must be a flaw in how I'm designing my methods. 我相信我如何设计我的方法一定存在缺陷。 Here's an example... 这是一个例子......

A Task contains Many Roles, each Role references a User. 任务包含许多角色,每个角色引用一个用户。

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

Considering that I would have queried my context like this by mistake and not loaded User ... 考虑到我会错误地查询我的上下文而没有加载用户 ...

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

And then I call this method... 然后我称这种方法......

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

I may have built this method with every intention to load Roles and User but next time I use it I may forget that I need both. 我可能已经构建了这个方法,每个意图加载角色和用户,但下次我使用它时,我可能会忘记我需要两者。 Ideally the method definition should tell me what data is necessary, but even if I pass in both Task and Roles, I'm still missing Roles->User. 理想情况下,方法定义应该告诉我哪些数据是必要的,但即使我传入任务和角色,我仍然缺少角色 - >用户。

What's the proper way to reference these relationships and be sure that they're loaded in something like this print method? 引用这些关系的正确方法是什么,并确保它们被加载到类似这种打印方法的东西中? I'm interested in a better design, so "Use Lazy Loading" isn't the answer I'm looking for. 我对更好的设计感兴趣,所以“使用延迟加载”不是我正在寻找的答案。

Thanks! 谢谢!

EDIT: 编辑:

I know I can load the task like this... 我知道我可以像这样加载任务......

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

What I want to know is how do I design my method so that when I come back and use it 6 months from now I know what data needs to be loaded in my entities? 我想知道的是我如何设计我的方法,以便当我回来并在6个月后使用它时,我知道需要在我的实体中加载哪些数据? The method definition doesn't indicate what is necessary to use it. 方法定义未指出使用它的必要条件。 Or how to I block against these NullReferences. 或者我如何阻止这些NullReferences。 There has to be a better design. 必须有一个更好的设计。

You can use the Select extension method to eager load Users . 您可以使用Select扩展方法来急切加载Users

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

Edit: 编辑:

There are few ways that I can think of to avoid the NRE 我无法想到避免NRE的方法

  • Integration test using a SQL Server CE/Express database. 使用SQL Server CE / Express数据库进行集成测试。 Unit testing with fake contexts will not work correctly. 使用伪上下文进行单元测试将无法正常工作。
  • Loading the entities close to where they are consumed. 将实体加载到消耗它们的位置附近。 So that the Include s are near to where the entities are used. 因此Include s接近使用实体的位置。
  • Passing DTOs/ViewModels to the upper layers without passing the entities. 将DTO / ViewModel传递到上层而不传递实体。

User should be lazily loaded in your loop—just note though that this is a classic select N + 1 problem that you should fix with another Include . User应该在你的循环中懒得加载 - 只是注意这是一个经典的选择N + 1问题,你应该用另一个Include修复。

I think the root problem is either that this particular Role doesn't have a User , or that this particular Role 's User has null set for its Name . 我认为问题的根源是,要么这个特殊的Role 没有一个User ,或者这个特定RoleUser有其空集Name You'll need to check both for null in your loop 您需要在循环中检查两者是否为null

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

Very good question. 非常好的问题。 Here are some possible solutions that, while they don't enforce the avoidance of NREs, they'll provide clues to the caller that they need to Include things: 以下是一些可能的解决方案,虽然它们不强制避免使用NRE,但它们会向调用者提供他们需要Include内容的线索:

The first option is to not have your method access a non-guaranteed property of an entity; 第一种选择是不让您的方法访问实体的非保证属性; rather, force the caller to pass both entities: 相反,强制调用者传递两个实体:

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

Another option is to name the parameter of your method such that it will clue the caller as to what is required: 另一种选择是命名方法的参数,以便它可以使调用者了解所需的内容:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 EF 5-6.1.1中的NullReferenceException,具有两个相同类型的导航属性 - NullReferenceException in EF 5-6.1.1 with two navigation properties to the same type EF 避免将导航属性附加到 DTO - EF avoid attaching navigation properties to the DTO 访问DBSet时出现NullReferenceException。 使用MySQL,EF6 - NullReferenceException when accessing DBSet. Using MySQL, EF6 使用Web Api和EF中的Linq访问具有导航属性的实体 - Accessing entities with navigation properties using Linq in Web Api and EF 方法上的多个NSubstitute调用配置访问引用类型参数上的不同属性(以避免NullReferenceException) - Multiple NSubstitute call configurations on method accessing different properties on a reference type parameter (to avoid NullReferenceException) 相关导航属性上的 EF Core NullReferenceException - EF Core NullReferenceException on Related Navigation Property 具有 EF 导航属性的 Automapper - Automapper with EF Navigation Properties EF导航属性不起作用? - EF Navigation Properties not working? 具有导航属性的EF SqlQuery - EF SqlQuery with navigation properties 访问某个对象时出现NullReferenceException - NullReferenceException when accessing some object
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM