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...
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.