【问题标题】:Is it possible to set up this relationship via the Fluent API?是否可以通过 Fluent API 建立这种关系?
【发布时间】:2018-11-24 15:28:17
【问题描述】:

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

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

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

两个实体:

    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 设置和任何最佳实践的 cmets。

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

【问题讨论】:

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


    【解决方案1】:

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

    一旦我这样做了,现在当父包实体被加载时,它会正确加载子包条目。

    所以,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);
    

    【讨论】:

    • 您能否添加您如何重新配置​​所有内容的代码?这对以后可能会发现这个问题的其他人更有帮助。
    【解决方案2】:

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

    我从未尝试将 IReadonlyCollection&lt;T&gt; 与 EntityFramework 一起使用,但我想 EF 也不会喜欢这样。

    您的第一次尝试应该是删除属性并像这样排列属性:

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

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

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

    因此,如果仍然无法正常工作,您应该创建 _packageEntries protected internal 并将其用作您的 EF 集合。那么你只能像现在这样公开你的PackageEntries

    【讨论】:

    • 首先谢谢您! - 我基于Microsoft docs 这个设计的一部分。实体正在按原样保存,我发现我错过了将顶级包添加到上下文中,一旦我这样做了,包条目就会被保存。我仍然不是 100% 我的事情映射正确......
    • 这是一种非常有趣的做事方式。看起来您仍然缺少 EF 设置中的 navigation 来告诉它支持字段。当我了解 EF 时,它不可用,而且非常有趣!如果你愿意,我可以更新我的答案,但我认为你有更多关于从这里去哪里的线索。
    猜你喜欢
    • 2017-07-22
    • 1970-01-01
    • 2014-08-13
    • 1970-01-01
    • 2011-06-09
    • 1970-01-01
    • 1970-01-01
    • 2020-04-06
    • 2015-01-14
    相关资源
    最近更新 更多