EF:加载相关实体懒惰的方式不起作用

EF: Loading Related Entities Lazy way dont work

我哪里错了?我在导航 属性 Events 中收到 NullReferenceExeption。它被标记为 virtual 并且 LazyLoadingEnabled 是明确允许的。

程序代码:

using (var db = new Db())
{
    var person = new Person();
    person.Name = "bla bla";
    db.Persons.Add(person);
    person.Events = new List<Event>(1);
    person.Events.Add(new Event() { Description = "ABC", At = DateTime.Now });
    db.SaveChanges();
}

using (var db = new Db())
{
    var person = db.Persons.First();

    Console.WriteLine(person.Events == null);  //Output: True
    Console.WriteLine(person.Events.Count);    //System.NullReferenceException
}

显式加载有效:

var person = db.Persons.First();
db.Entry(person).Collection(p => p.Events).Load();
Console.WriteLine(person.Events == null);  //Output: False
Console.WriteLine(person.Events.Count);    //1

类结构:

class Db : DbContext
{
    public Db()
    {
        Database.SetInitializer(new DropCreateDatabaseAlways<Db>());
        Configuration.LazyLoadingEnabled = true;
    }

    public DbSet<Person> Persons { get; set; }
    public DbSet<Event> Events { get; set; }
}

class Person
{
    public int id { get; set; }
    public string Name { get; set; }

    public virtual List<Event> Events { get; set; }
}

class Event
{
    public int id { get; set; }
    public DateTime At { get; set; }
    public string Description { get; set; }

    public Person Person { get; set; }
    public int PersonId { get; set; }
}

很简单。

在 Person class 中创建默认构造函数并初始化 Events 集合,如

public Person()
{
   Events = new HashSet<Event>();
}

并将类型为 Person 的虚拟 属性 添加到事件 class

编辑: 在 EF codefirst 中,您负责集合初始化,而 EF 负责从数据库中填充此集合

new Person() 替换为 context.Persons.Create()

这将为您提供 Entity Framework 个人代理,而不是 Person。此代理 class 派生自 Person,但其 Events 属性 已被延迟加载逻辑覆盖。

但是,这并不能解决您代码中的主要问题。这是上下文的重用。你不应该这样做。

您的模型 类 需要 public 才能延迟加载。将 class Personclass Event 更改为 public class Person 等,您的测试程序将开始工作。

您的代码没有任何问题:如果您对空集合执行 Count,您将始终得到 NullReferenceException - 这与 Entity Framework 无关,那是 .NET .

null 集合和空集合是有区别的。

现在,如果您想避免此类 NullReferenceException 异常,您始终可以在默认构造函数中启动列表,如:

class Person
{
    public Person()
    {
        Events = new List<Event>();
    }

    public int id { get; set; }
    public string Name { get; set; }

    public virtual List<Event> Events { get; set; }
}
public class Person
{
    public Person()
    {
          Events = new List<Event>();
    }

    public int id { get; set; }
    public string Name { get; set; }

    public virtual List<Event> Events { get; set; }
}

public class Event
{    
    public int id { get; set; }
    public DateTime At { get; set; }
    public string Description { get; set; }

    public Person Person { get; set; }
    public int PersonId { get; set; }
}

您需要使用相同类型的集合来初始化任何集合导航属性,而不是 'HashSet',就像这样。