首页 > 解决方案 > EF Core 5 - 如何向多对多关系添加自定义字段以指示乘数?

问题描述

我正在使用 .NetCore Razor Pages 构建一个网站,就像一个作品集,用户可以在其中上传一些照片,然后将它们显示在不同的页面上。每个页面都可以通过一些属性来定制,这些属性存储在数据库中图像的路径旁边。为了加快每个页面的构建阶段,我想实现行模板。

我有两个类:一个代表模板,另一个代表模板的组件。我的目标是使用先前创建的组件构建不同的模板,然后使用它们创建已经拆分为 TemplateComponent 空间属性长度的(引导)列的行。

到目前为止,EFCore 创建了实体来创建模板和组件之间的关系,但是这样每个组件只能与每个模板建立一次关系,从而拒绝使用 2 倍长度的组件构建模板的能力6.

如何编辑关系,以添加在同一组件上多次构建模板的机会?

到目前为止,我有这两个模型(以及它们各自在 DB 中的实体,由 EF Core 创建)和 DbContext 类:

public class Template
    {
        [Key]
        public int Id { get; set; }

        [Required]
        public string ImagePath{ get; set; }


        public List<TemplateComponent> Components { get; set; }

    }

public class TemplateComponent
    {
        [Key]
        public int Id { get; set; }

        [Range(0,12)]
        public int Space{ get; set; }

        public List<Template> Rows{ get; set; }

    }


public class ApplicationDbContext : IdentityDbContext
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }

        public DbSet<TemplateComponent> TemplateComponents{ get; set; }

        public DbSet<Template> Templates{ get; set; }
    }
}

标签: entity-frameworkasp.net-core.net-coreentity-framework-core

解决方案


在幕后 EFCore 5 将创建一个链接表,可能称为 TemplateComponentTemplates 或 TemplateTemplateComponents。该表将仅包含一个 TemplateId 和一个 TemplateComponentId。要将任何其他列附加到此表中,例如数量或支持同一模板上的多个组件,您将需要显式声明此连接实体。

为了简化命名约定,我将您所称的 TemplateComponent 重命名为 Component,这样 TemplateComponent 将反映连接实体。

第一步是更新我们的“多”引用类型以使用连接实体:

public class Template
{
    [Key]
    public int Id { get; set; }

    [Required]
    public string ImagePath{ get; set; }


    public virtual ICollection<TemplateComponent> TemplateComponents { get; set; } = new List<TemplateComponent>();

}

public class Component
{
    [Key]
    public int Id { get; set; }

    [Range(0,12)]
    public int Space{ get; set; }

    public virtual ICollection<TemplateComponent> TemplateComponents { get; set; } = new List<TemplateComponent>();

}

应声明“多”侧导航属性,virtual ICollection<TEntity>以便 EF 可以正确连接代理以进行更改跟踪。

现在,根据您希望如何表达模板和组件之间的多重性,TemplateComponent 实体可能类似于:

public class TemplateComponent
{
    [Key, Column(Order-0), ForeignKey(Template)]
    public int TemplateId { get; set; }
    [Key, Column(Order-1), ForeignKey(Component)]
    public int Component { get; set; }

    public int Quantity { get; set; } = 1;

    public virtual Template Template { get; set; }
    public virtual Component Component { get; set; }
}

在组件和特定模板之间会有一个链接记录,但有一个附加字段来表示该模板和组件关联的乘数或数量。

或者,如果您想支持可以为每个组合设置此连接记录的多个实例的连接(可能为每个组合提供附加信息列):

public class TemplateComponent
{
    [Key]
    public int Id { get; set; }

    [ForeignKey(Template)]
    public int TemplateId { get; set; }
    [ForeignKey(Component)]
    public int Component { get; set; }

    public virtual Template Template { get; set; }
    public virtual Component Component { get; set; }
}

这里的区别在于 TemplateComponent 获得了自己唯一的 PK 而不是复合键,允许一个 Component 为同一个 Template 拥有多个 ComponentTemplate。

在这两种情况下,实体之间的关系映射都会发生一些变化。代替:

modelBuilder.Entity<Template>()
    .HasMany(x => x.Components)
    .WithMany(x => x.Templates);

在 EF Core 自动连接一个连接表的地方,您需要更明确一点:

modelBuilder.Entity<Template>()
   .HasMany(x => x.TemplateComponents)
   .WithOne(x => x.Template)
   .IsRequired();
modelBuilder.Entity<Compoent>()
   .HasMany(x => x.TemplateComponents)
   .WithOne(x => x.Component)
   .IsRequired();

或者它可以从 TemplateComponent 端连接。

现在,您可以访问 Template.TemplateComponents 以获取包含 Quantity 属性或同一组件的多行的组件引用集合。可以从组件中完成相同的操作。


推荐阅读