【问题标题】:Many-to-Many relationship in EF Core with dependent and principal entity both being the same typeEF Core 中的多对多关系,依赖实体和主体实体都是同一类型
【发布时间】:2023-03-07 17:17:01
【问题描述】:

想象这个类用于实体框架的数据模型:

public class CriteriaCatalogue : BaseDataModel
{
    public string Name { get; set; }

    public List<CriterionToCriteriaCatalogueLink> CriteriaLinks { get; set; }

    public virtual ICollection<CriteriaCatalogueToCriteriaCatalogueLink> CatalogueChildren { get; set; }
    public virtual ICollection<CriteriaCatalogueToCriteriaCatalogueLink> CatalogueParents { get; set; }
    // a bunch of additional properties with custom getters and setters to more easily access and create the child/parent `CriteriaCatalogue`s
}

CriteriaLinks 属性被完美填充,但我的父子列表链接到相同类型根本不起作用,它们填充了相同的对象(因此在查看 Owner1 时,两个子项都将是 Owner1父母将是 Owner1)

因此,虽然以下代码是有效代码,并且只要您不让 EF 触摸它就会按预期工作,但 EF 将删除其中一个连接:

CriteriaCatalogue owned = new CriteriaCatalogue { Name = "Owned" };
CriteriaCatalogue owner1 = new CriteriaCatalogue { Name = "Owner1", CriteriaCatalogues = new List<CriteriaCatalogue> { owned } };
CriteriaCatalogue owner2 = new CriteriaCatalogue { Name = "Owner2", CriteriaCatalogues = new List<CriteriaCatalogue> { owned } };

如果通过 EF 将所有三个对象保存到数据库中,结果如下: owned 没有任何孩子或父母。 owner1 将 owner1 作为孩子,将 owner1 作为父母。 owner2 拥有 owner2 作为孩子,拥有 owner2 作为父母。

如何正确配置 EF 以允许 owned 可以属于多个其他实体?

编辑 2:我让它工作了。我需要做的就是在 OnModelCreating 方法中切换 CatalogueChildren 和 CatalogueParents 引用:

        modelBuilder.Entity<CriteriaCatalogueToCriteriaCatalogueLink>(cctcc =>
        {
            cctcc.HasKey(cctcc => new { cctcc.ChildId, cctcc.ParentId });

            cctcc.HasOne(cctcc => cctcc.Child)
                .WithMany(children => children.CatalogueParents)
                .HasForeignKey(cctcc => cctcc.ChildId)
                .OnDelete(DeleteBehavior.ClientSetNull);

            cctcc.HasOne(cctcc => cctcc.Parent)
                .WithMany(parents => parents.CatalogueChildren)
                .HasForeignKey(cctcc => cctcc.ParentId);
        });

编辑:根据 Sergey 的回答,这是我的完整实现:​​

链接类:

public class CriteriaCatalogueToCriteriaCatalogueLink
{
    public Guid ParentId { get; set; }
    public virtual CriteriaCatalogue Parent { get; set; }
    public Guid ChildId { get; set; }
    public virtual CriteriaCatalogue Child { get; set; }
}

DbContext.OnModelCreating sn-p:

        modelBuilder.Entity<CriteriaCatalogue>(criterionCatalogue =>
        {
            criterionCatalogue.HasMany(cc => cc.CriteriaLinks);
            criterionCatalogue.HasMany(cc => cc.CatalogueChildren);
            criterionCatalogue.HasMany(cc => cc.CatalogueParents);
        });

        modelBuilder.Entity<CriterionToCriteriaCatalogueLink>(ctcc => 
        { 
            ctcc.HasKey(ctcc => new { ctcc.CriteriaCatalogueId, ctcc.CriterionId });

            ctcc.HasOne(ctcc => ctcc.Criterion)
                .WithMany(criterion => criterion.CriteriaCatalogueLinks)
                .HasForeignKey(ctcc => ctcc.CriterionId);

            ctcc.HasOne(ctcc => ctcc.CriteriaCatalogue)
                .WithMany(criteriaCatalogue => criteriaCatalogue.CriteriaLinks)
                .HasForeignKey(ctcc => ctcc.CriteriaCatalogueId);
        });

        modelBuilder.Entity<CriteriaCatalogueToCriteriaCatalogueLink>(cctcc =>
        {
            cctcc.HasKey(cctcc => new { cctcc.ChildId, cctcc.ParentId });

            cctcc.HasOne(cctcc => cctcc.Child)
                .WithMany(children => children.CatalogueChildren)
                .HasForeignKey(cctcc => cctcc.ChildId)
                .OnDelete(DeleteBehavior.ClientSetNull);

            cctcc.HasOne(cctcc => cctcc.Parent)
                .WithMany(parents => parents.CatalogueParents)
                .HasForeignKey(cctcc => cctcc.ParentId);
        });

如您所见,连接Criterions 的实现有点不同,但基本相同。我尝试了完全相同的方法来连接CriteriaCatalogues,但它不起作用。我认为当我尝试这样做时,EF 给了我一个关于圆圈引用的错误。最重要的是,一个CriteriaCatalogue 仍然不可能有多个父母。

【问题讨论】:

  • 很抱歉,我对 google 的内容没有任何想法,我确信这只是 EF 模型中的一个选项。
  • 我对您的示例有疑问,在调用SaveChanges 之后,objectA.Id1 还是2?子对象不能同时有两个父对象。
  • 在这种情况下,ID 并不重要。无论如何,它们都是随机生成的 GUID。而且,关于它是否可能,它肯定是。我只需要弄清楚如何设置 EF 来处理它。正如您在我的实现中看到的,我已经设置了一个 Linking 类(所以从技术上讲,是的,一个子对象没有两个直接的父对象,但我们可以设置一个基本实现相同的模型)
  • 您将一个对象作为子对象引用到其他两个父对象。保存数据后,显示插入 2 条记录,但在代码中,有两个 GUID 分配给 Id 属性,这些记录与一个对象相关。如果你删除objectA,那么objectB的子对象会被删除,或者objectC或者EF应该删除数据库中的两行来删除一个对象?
  • 这个例子可能有点混乱,因为我使用该属性的设置器(CriteriaCatalogues)来创建链接对象,并将它们存储在单独的列表中(例如CatalogueChildren),并且 getter 只公开 CriteriaCatalogues 的列表而不是链接对象的列表

标签: c# entity-framework .net-core entity-framework-core


【解决方案1】:

你可以试试这个代码:

创建一个新类

public class MyClassMyClass
{
    public int ParentId { get; set; }
    public int ChildId { get; set; }

    public virtual MyClass Parent { get; set; }

    public virtual MyClass Child { get; set; }

}

并将此代码添加到 dbcontext:

modelBuilder.Entity<MyClassMyClass>(entity =>
            {
              
                entity.HasOne(d => d.Child)
                    .WithMany(p => p.MyClassMyClassChildren)
                    .HasForeignKey(d => d.ChildId)
                    .OnDelete(DeleteBehavior.ClientSetNull);

                entity.HasOne(d => d.Parent)
                    .WithMany(p => p.MyClassMyClassParents)
                    .HasForeignKey(d => d.ParentId);
            });

修复你的课程:

public partial class MyClass
    {
       
        public int Id { get; set; }

        public virtual ICollection<MyClassMyClass> MyClassMyClassChildren { get; set; }
        public virtual ICollection<MyClassMyClass> MyClassMyClassParents { get; set; }
    }

我在 VS2019 中使用此代码对其进行了测试:

MyClass objectA = new MyClass();
var objectB = new MyClassMyClass { Parent = new MyClass(), Child = objectA };
var objectC = new MyClassMyClass { Parent = new MyClass(), Child = objectA };


_context.MyClassMyClasses.AddRange(new MyClassMyClass[] { objectB, objectC });
 _context.SaveChanges();

一切都很完美。

【讨论】:

  • 我试过这样实现,但还是不行。但是,当父母和孩子是不同的班级时,它可以正常工作。我将在我的问题中展示我的完整实现。
  • @lenny 很抱歉,但是父母和孩子是同一个班级,他们只是名字不同。您不能将具有相同类型和名称的两个属性放在一个类中。所以它回答了你的问题,你必须接受答案。你的答案是我的副本。你刚刚改了名字。
  • 如果我能以你的想法为概念,我会接受你的回答。目前,它不适合我。我什至赞成你的努力,但只要它在我的机器上不起作用,我就不会接受。无论如何,您认为我的实施到底有什么问题?运行该代码时,owner1 将 owner1 作为父级,owner1 作为子级,而 owner2 将 owner2 作为父级,owner2 作为子级。拥有的不会有任何孩子或父母。
  • 您是否按原样尝试了我的答案?
  • 是的,据我了解,我当前的实现基本上是完全相同的(除了一些 getter/setter 的乐趣,这使得访问孩子/父母更容易)。也许 OnModelCreating 让我搞砸了?请注意,我有一个非常相似的概念来处理链接的不同类。
猜你喜欢
  • 2019-11-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-08
  • 1970-01-01
  • 1970-01-01
  • 2020-10-28
  • 1970-01-01
相关资源
最近更新 更多