如何在实体框架中创建多对多映射?

Posted

技术标签:

【中文标题】如何在实体框架中创建多对多映射?【英文标题】:How to create a many-to-many mapping in Entity Framework? 【发布时间】:2013-10-20 23:53:59 【问题描述】:

这里是这样,我有2个实体,比如Contract、Media。

public class Media : Entity

    public string Name get; set;
    public bool Enabled
    *//other properties can be ignored..*


public class Contract : Entity

    public string Code get; set;
    *//other properties can be ignored..*

Contract 有很多 Media,看起来是多对多的。

但是!!首先在 ef 代码中,我需要 ContractMedia 表中的另外 3 个字段(ef 自动生成)。 例如开始日期、结束日期和价格。这些无法添加到媒体实体中。

在这种情况下如何映射??

【问题讨论】:

Create code first, many to many, with additional fields in association table的可能重复 【参考方案1】:

如果要与关联表中的附加数据创建多对多关系,则必须将关联表设为实体。纯多对多关系仅在具有实体 id 的纯表中。

在你的情况下,它将是:

public class Media // One entity table

    public int Id  get; set; 
    public string Name  get; set; 
    public bool Enabled  get; set; 

    public virtual ICollection<ContractMedia> ContractMedias  get; set; 


public class Contract // Second entity table

    public int Id  get; set; 
    public string Code  get; set 

    public virtual ICollection<ContractMedia> ContractMedias  get; set; 


public class ContractMedia // Association table implemented as entity

    public int MediaId  get; set; 
    public int ContractId  get; set; 
    public DateTime StartDate  get; set; 
    public DateTime EndDate  get; set; 
    public double Price  get; set; 

    public virtual Media Media  get; set; 
    public virtual Contract Contract  get; set; 

在创建模型/实体之后,您需要在上下文中定义关系:

protected override void OnModelCreating(DbModelBuilder modelBuilder)

   modelBuilder.Entity<ContractMedia>()
       .HasKey(c => new  c.MediaId, c.ContractId );

   modelBuilder.Entity<Contract>()
       .HasMany(c => c.ContractMedias)
       .WithRequired()
       .HasForeignKey(c => c.ContractId);

   modelBuilder.Entity<Media>()
       .HasMany(c => c.ContractMedias)
       .WithRequired()
       .HasForeignKey(c => c.MediaId);  

您也可以参考以下链接:Many to many mapping with extra fields in Fluent APIEntity Framework CodeFirst many to many relationship with additional informationCreate code first, many to many, with additional fields in association table

【讨论】:

我认为ContractMedia 不应该有反向导航集合:Medias & Contracts。这些应该是前向导航属性。在我将反向(集合)属性更改为正向属性之前,我在多对多查找表中获得了额外的字段。 @IAbstract 我认为您可能是对的,因为我没有从导航到公用表中检索值。我确信这是由于导航集合 @IAbstract:如何将“反向(集合)属性更改为正向属性。”? 属性有可能吗? @IAbstract 为了没有双字段,您必须在每一端链接两个导航属性。您可以通过使用 inverseproperty 属性来实现这一点。对于此示例,您可以添加 [InverseProperty("Medias ")] public virtual ICollection ContractMedias get;放; 在媒体类中【参考方案2】:

无需使用 Fluent API 即可添加到 @Tomas 答案。

public class Media // One entity table

    public int Id  get; set; 

    public string Name  get; set; 

    public virtual ICollection<ContractMedia> ContractMedias  get; set; 


public class Contract // Second entity table

    public int Id  get; set; 

    public string Code  get; set 

    public virtual ICollection<ContractMedia> ContractMedias  get; set; 


public class ContractMedia // Association table implemented as entity

    [Key]
    [Column(Order = 0)]
    [ForeignKey("Media")]
    public int MediaId  get; set; 

    [Key]
    [Column(Order = 1)]
    [ForeignKey("Contract")]
    public int ContractId  get; set; 

    public DateTime StartDate  get; set; 

    public DateTime EndDate  get; set; 

    public double Price  get; set; 

    public virtual Media Media  get; set; 

    public virtual Contract Contract  get; set; 

EF Core 需要使用 Fluent API,但它看起来像这样:

internal class MyContext : DbContext

    public MyContext(DbContextOptions<MyContext> options)
        : base(options)
    
    

    public DbSet<Post> Posts  get; set; 
    public DbSet<Tag> Tags  get; set; 

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    
        modelBuilder.Entity<Post>()
            .HasMany(p => p.Tags)
            .WithMany(p => p.Posts)
            .UsingEntity<PostTag>(
                j => j
                    .HasOne(pt => pt.Tag)
                    .WithMany(t => t.PostTags)
                    .HasForeignKey(pt => pt.TagId),
                j => j
                    .HasOne(pt => pt.Post)
                    .WithMany(p => p.PostTags)
                    .HasForeignKey(pt => pt.PostId),
                j =>
                
                    j.Property(pt => pt.PublicationDate).HasDefaultValueSql("CURRENT_TIMESTAMP");
                    j.HasKey(t => new  t.PostId, t.TagId );
                );
    


public class Post

    public int PostId  get; set; 
    public string Title  get; set; 
    public string Content  get; set; 

    public ICollection<Tag> Tags  get; set; 
    public List<PostTag> PostTags  get; set; 


public class Tag

    public string TagId  get; set; 

    public ICollection<Post> Posts  get; set; 
    public List<PostTag> PostTags  get; set; 


public class PostTag

    public DateTime PublicationDate  get; set; 

    public int PostId  get; set; 
    public Post Post  get; set; 

    public string TagId  get; set; 
    public Tag Tag  get; set; 

来源:

https://docs.microsoft.com/en-us/ef/core/modeling/relationships?tabs=fluent-api%2Cfluent-api-simple-key%2Csimple-key#join-entity-type-configuration

【讨论】:

以上是关于如何在实体框架中创建多对多映射?的主要内容,如果未能解决你的问题,请参考以下文章

如何创建如何在 typeorm 中创建多对多关系,[NestJS]

如何在rails的同一个表中创建多对多

Hibernate 与同一实体的递归多对多关联

如何使用 schema.graphql 在放大数据存储中创建多对多关系

在控制器中创建多对多模型的模式

使用嵌套字段在MongoDB / Mongoose中创建多对多关系?