Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
320 views
in Technique[技术] by (71.8m points)

entity framework - EF Core 5 - How can I add to a Many To Many Relationship a custom field to indicate a multiplier?

I'm building a website with .NetCore Razor Pages, pretty much like a portfolio, where users can upload some photos and then display them on different pages. Every page can be customized through some properties which are stored alongside the path of the image in the DB. To speed up the building phase of each page, I thought to implement row templates.

I have two classes: one represents the template and the other a component of the template. I aim to build different templates using previously created components, and later use them to create rows already split into (bootstrap) columns of the length of TemplateComponent space property.

As of now, EFCore created the entity to create the relationship between Template and component, but in this way every component can be put in relationship with every template only once, turning down the ability to have a template built using 2 times the component of length 6.

How can I edit the relationship, to add the opportunity of having a template built upon the same component multiple times?

As of now i have these two models (and their respective entity in DB, created by EF Core) and the DbContext class:

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; }
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Behind the scenes EFCore 5 will be creating a linking table, likely called TemplateComponentTemplates or TemplateTemplateComponents. This table will solely consist of a TemplateId and a TemplateComponentId. To append any additional columns to this table such as a quantity or accommodate support for multiple components on the same template, you will need to explicitly declare this joining entity.

To simplify the naming convention I've renamed what you call a TemplateComponent to Component so that TemplateComponent will reflect the joining entity.

The first step is to update our "many" reference type to use the joining entity:

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>();

}

"Many"-side navigation properties should be declared as virtual ICollection<TEntity> so that EF can properly wire up proxies for change tracking.

Now, depending on how you want the multiplicity between template and component expressed, the TemplateComponent entity could be something like:

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; }
}

There would be one linking record between a Component and particular template but with an additional field to express a multiplier or quantity of that template and component association.

Or instead if you want to support a join where multiple instances of this joining record can be set up for each combination (likely with additional information columns for each combination):

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; }
}

The difference here is that TemplateComponent gets its own unique PK rather than the composite key, allowing for a Component to have more than one ComponentTemplate for the same Template.

In both cases the mapping of the relationship between the entities changes a bit. Instead of:

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

Where EF Core auto-magically wires up a joining table, you need to be a bit more explicit:

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

Or it could be wired from the TemplateComponent side.

Now you can access Template.TemplateComponents to get the collection of component references containing either the Quantity property, or multiple rows for the same component as desired. The same can be done from the Component.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share
...