【问题标题】: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 自动生成)。 例如开始日期、结束日期和价格。这些无法添加到媒体实体中。

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

【问题讨论】:

标签: c# entity-framework many-to-many


【解决方案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 API
Entity Framework CodeFirst many to many relationship with additional information
Create 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-01-07
    • 1970-01-01
    • 2014-10-20
    • 2017-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多