是否可以通过Fluent API设置此关系?

时间:2018-06-14 19:21:46

标签: ef-code-first entity-framework-core ef-fluent-api

我有两个代码第一个实体,Package和PackageEntry,我在EF Core中设置时遇到了问题。

我正在尝试使用代码优先实体和Fluent API实现以下功能:

  • 包可以包含任意数量的PackageEntries
  • 每个PackageEntry都引用一个Package实体(一个包的另一个实例,与包含PackageEntries集合的父包引用无关)

这两个实体:

    public class Package{   
      public Package()
      {
        _packageEntries = new List<PackageEntry>();
      }
      //trimmed other properties

      private readonly List<PackageEntry> _packageEntries;

      [NotMapped]
      public IReadOnlyCollection<PackageEntry> PackageEntries => _packageEntries.ToList().AsReadOnly();

      }

public class PackageEntry
{
    public int DisplayOrder { get; set; }
    public int PackageID { get; set; }
    public Package Package { get; set; }
    public int Quantity { get; set; }
    public Package ParentPackage { get; set; }
    public int ParentPackageID { get; set; }
}

我目前使用Fluent API的方法是:

modelBuilder.Entity<Package>().HasMany(x => x.PackageEntries).WithOne();
modelBuilder.Entity<PackageEntry>().HasOne(x => x.Package).WithOne().HasForeignKey(typeof(PackageEntry), "PackageID");

它不会抛出错误,但我所看到的是,当一个PackageEntry被添加到一个包时,它在上下文中调用SaveChanges时不会被保存。

我是否在使用Fluent API或其他方面做错了什么?

修改 我错过了将顶级包添加到上下文中,一旦完成,就会保存添加到其中的包条目。我仍然会对Fluent API设置和任何最佳实践提出意见。

在PackageEntry实体中,我需要知道父包和包含的包,它们将是对同一类型的单独引用。我似乎无法将其设置为Fluent API,当通过EF加载父包时,即使正确设置了ParentPackageID,它也不包含任何PackageEntry对象。

2 个答案:

答案 0 :(得分:1)

根据EF专家的一些离线建议,我通过删除PackageEntry.Package的导航属性并只需手动处理该包实体的外键来解决此问题。

一旦执行完此操作,现在在加载“父包”实体时,它将正确加载子PackageEntries。

因此,PackageEntry类现在看起来像这样:

public class PackageEntry
{
    public int DisplayOrder { get; set; }
    public int PackageID { get; set; }
    //public Package Package { get; set; } //Handle manually
    public int Quantity { get; set; }
    public Package ParentPackage { get; set; }
    public int ParentPackageID { get; set; }
}

以及Fluent API代码:

navigation = builder.Metadata.FindNavigation(nameof(Package.PackageEntries));
//EF access the PackageEntries collection property through its backing field
navigation.SetPropertyAccessMode(PropertyAccessMode.Field);

modelBuilder.Entity<Package>().HasMany(x => x.PackageEntries)
            .WithOne("ParentPackage")
            .HasForeignKey(nameof(PackageEntry.ParentPackageID))
            .IsRequired()
            .OnDelete(DeleteBehavior.Restrict);

答案 1 :(得分:0)

您的Package.PackageEntries集合已标记为[NotMapped],并且没有设置者。无论如何,EntityFramework都不会选择它。

我从未尝试过使用带有EntityFramework的IReadonlyCollection<T>,但我认为EF也不会喜欢它。

您的第一次尝试应该是删除属性并按如下方式排列属性:

public virtual IReadOnlyCollection<PackageEntry> PackageEntries {
    get {
        return _packageEntries.ToList().AsReadonly();
    }
    protected internal set {
        _packageEntries = value;
    }
}

当然,这需要您从私有成员变量中删除readonly

话虽如此,我不确定EF是否有最终分配给该属性的内部列表,但我想它只会在集合上调用Add()方法(这就是为什么你的属性必须为ICollection<T>而不是IEnumerable<T>

因此,如果仍然无效,您应该_packageEntries protected internal并将其用作您的EF集合。然后,您只能公开公开PackageEntries,就像现在一样。