【问题标题】:Why navigation property doesn't populate when its class is private?为什么导航属性在其类为私有时不填充?
【发布时间】:2016-01-20 13:20:45
【问题描述】:

我有这个简单的模型:

class Parent
{
   public int Id { get; set; }

   public virtual ICollection<Child> Children { get; set; }
}

class Child
{
   public int Id { get; set; }
   public int ParentId { get; set; }

   public virtual Parent Parent { get; set; }
}

class MyContext : DbContext
{
    public DbSet<Parent> Parents { get; set; }
    public DbSet<Child> Children { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Child>().HasRequired(s => s.Parent).WithMany(s => s.Children).HasForeignKey(s => s.ParentId);

        base.OnModelCreating(modelBuilder);
    }
}

当我如下使用MyContext 时,我得到空引用异常,因为child.Parent 为空

var context = new MyContext();
var child = context.Children.First();
var parentId = child.Parent.Id;              // Parent == null

要解决这个问题,我必须将ParentChild 类的访问修饰符更改为public

为什么需要这样做?或者这只是一个错误?

【问题讨论】:

标签: c# entity-framework


【解决方案1】:

这不是错误,您正在使用实体框架的一个名为Lazy Loading 的功能,要使用它,您需要满足一些可以在此link 中找到的要求。其中一项要求是您的实体类必须是public。在该链接中,您将找到有关为什么应满足这些要求的正确解释,但总而言之,您的问题是因为 EF 无法从您的实体创建代理类,因为您不能使用延迟加载。您已经满足延迟加载的主要要求,即您的导航属性必须是 virtual,但首先您必须满足 EF 创建代理类所需的要求。

作为附加资源,我建议您查看 msdn 页面,您可以在其中找到使用 EF 加载相关实体的所有方法。

【讨论】:

  • 附带说明,一个好的经验法则是,您希望您的类具有与类的属性和方法的最高访问级别相同的访问级别。
  • @NathanC 不一定。想象一个私有嵌套类。如果它的所有成员也是私有的,则包含类不能访问该类。但是,您可以将成员更改为内部的,甚至在 API 的意义上是公共的,以在包含的类中提供访问。
  • @HimBromBeere 这是一个公平的对位。我在考虑一般的经验法则,而不是硬性规则。如果一个成员的访问权限比类本身更宽松,这应该是一个有目的的决定。
  • 非常有用的链接解释了延迟加载/代理创建的规则。最近几天我遇到了一些涉及私有构造函数的微妙错误。现在我已经解决了,但我肯定会保存它以供将来参考。
  • @NathanC 特别是当您的类实现强制其所有成员公开的接口时,您所说的内容并不适用。但是,您可能希望将类访问权限限制为 internal 甚至 private
【解决方案2】:

首先,澄清一下:您的课程不是private,而是internal。您不能将顶级类型(非嵌套类型)声明为私有(有关更多信息,请参阅MSDN Access Modifiers)。

其次,默认情况下,代码优先启用延迟加载。正如 octaviocci 所提到的,如果您希望使用延迟加载,则需要将它们声明为 public:这只是要求的一部分。通过将 Navigation 属性声明为 virtual,您就是在告诉 EF 您希望使用延迟加载。如果您不想这样做,您可以(应该)删除 virtual 关键字。然后,在获取实体时,使用Include 方法急切地加载相关实体。

var context = new MyContext();
var child = context.Children.Include(c => c.Parent).First();
var parentId = child.Parent.Id;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-31
    • 1970-01-01
    相关资源
    最近更新 更多