简体   繁体   中英

Entity Framework Code-first Many to many relationship on the same table

I have a slightly odd scenario here, I have one entity and one many to many table. The following tables are like so:

AssetHeader.cs

[Table("AssetHeader", Schema = "assets")]
public class AssetHeader
{
    public Guid Id { get; set; }

    public virtual ICollection<AssetHeaderEquipment> AssetHeaders { get; set; }

    public virtual ICollection<AssetHeaderEquipment> AssetEquipment { get; set; }
}

AssetHeaderEquipment.cs (many-to-many)

[Table("AssetHeaderEquipment", Schema = "assets")]
public class AssetHeaderEquipment
{
    [Key, Column(Order = 0)]
    public Guid AssetHeaderId { get; set; }
    [Key, Column(Order = 1)]
    public Guid AssetEquipmentId { get; set; }

    public virtual AssetHeader AssetHeader { get; set; }
    public virtual AssetHeader AssetEquipment { get; set; }

    public DateTime StartDate { get; set; }
    public DateTime? EndDate { get; set; }

    [MaxLength(255)]
    public string Comment { get; set; }
}

When I attempt to create the db with code-first migration I get the following in SQL: 在此输入图像描述

How do I get the code-first to generate the correct db structure, I am happy to use either attributes or fluent in dbcontext.

UPDATE: After following the answer from Ivan Stoev the structure is as expected. However when setting the fields the following now happens...

Model attempting to save: 在此输入图像描述

Actual save result: 在此输入图像描述

How is the AssetEquipmentId being set to the AssetHeaderId?

I have created a very lightweight dojo project for complete code visibility. You can find this here: GitHub Code Repo

This is one of the cases where EF can't automatically pair the relationships navigation properties.

Usually it can be resolved with either InverseProperty attributes or Has / With fluent API. But since this type of relationship always introduces "multiple cascade paths" problem, which requires turning off cascade delete for at least one of the relationships, and that could be done only with fluent API, there is no other choice than using fluent API (which for relationships IMHO is better anyway).

The bare minimum is to configure the relationship for which you want to turn off the cascade delete. Once you do that, EF conventionally will do the rest.

For instance:

modelBuilder.Entity<AssetHeader>()
    .HasMany(e => e.AssetEquipment)
    .WithRequired(e => e.AssetEquipment)
    .WillCascadeOnDelete(false);

The result:

CreateTable(
    "assets.AssetHeaderEquipment",
    c => new
        {
            AssetHeaderId = c.Guid(nullable: false),
            AssetEquipmentId = c.Guid(nullable: false),
            StartDate = c.DateTime(nullable: false),
            EndDate = c.DateTime(),
            Comment = c.String(maxLength: 255),
        })
    .PrimaryKey(t => new { t.AssetHeaderId, t.AssetEquipmentId })
    .ForeignKey("assets.AssetHeader", t => t.AssetEquipmentId)
    .ForeignKey("assets.AssetHeader", t => t.AssetHeaderId, cascadeDelete: true)
    .Index(t => t.AssetHeaderId)
    .Index(t => t.AssetEquipmentId);

CreateTable(
    "assets.AssetHeader",
    c => new
        {
            Id = c.Guid(nullable: false),
        })
    .PrimaryKey(t => t.Id);

Update According to the problematic usage case, the correct pairings should be:

Collection       Inverse          FK
==========       =========        ==============
AssetHeaders     AssetEquipment   AssetEquipmentId
AssetEquipment   AssetHeader      AssetHeaderId

Hence remove the misleading ForeignKey attributes and use the following fully explicit fluent configuration:

modelBuilder.Entity<AssetHeader>()
    .HasMany(e => e.AssetHeaders)
    .WithRequired(e => e.AssetEquipment)
    .HasForeignKey(e => e.AssetEquipmentId)
    .WillCascadeOnDelete(false);

modelBuilder.Entity<AssetHeader>()
    .HasMany(e => e.AssetEquipment)
    .WithRequired(e => e.AssetHeader)
    .HasForeignKey(e => e.AssetHeaderId)
    .WillCascadeOnDelete(true);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM