EF Core - How to configure a required Foreign Key without a cascaded delete

Viewed 140

I'm getting troubles with quite a simple thing. (Entity Framework Core 5)

I wrote following configuration class for a base model from which several child classes are derived.

Models:

abstract public class RecordBase
{
    public Guid Id { get; set; }
}

public class Item : RecordBase
{
    public string name { get; set; }
}

[NotMapped]
public abstract class LineBase : RecordBase
{
    public Guid ItemId { get; set; }
    public Item Item { get; set; }
    public string Unit { get; set; }
    public double Qty { get; set; }
    public virtual ICollection<ItemTransaction> ItemTransactions { get; set; } = new Collection<ItemTransaction>();
    public virtual ProductDim ProductDim { get; set; } 
}

public abstract class OrderLineBase : LineBase
{
    public string OrderNum { get; set; }
}

public abstract class JournalLineBase : LineBase
{
    public string JournalNum { get; set; }
}

public class SalesOrderLine : OrderLineBase
{
    public string CustomerNum { get; set; }
}

public class PurchOrderLine : OrderLineBase
{
    public string CustomerNum { get; set; }
}

public class WmsJournalLine : JournalLineBase
{
    public string Warehouse { get; set; }
}

public class ItemTransaction : RecordBase
{
    public DateTime TransDateTime { get; set; }
    public virtual LineBase Line { get; set; }
    public string Reference{ get; set; }
}

public class ProductDim : RecordBase
{
    public int Configuration { get; set; }
    public virtual ICollection<LineBase> LineBases { get; set; }
}

So I'm defining the relation to the ProductDim Table , which must not be nullable. So I set up the relation make it IsRequired() and set the DeleteBehavior to Restrict.

class LineBaseConfiguration<TEntity> : IEntityTypeConfiguration<TEntity>
    where TEntity : LineBase
{
    public virtual void Configure(EntityTypeBuilder<TEntity> modelBuilder)
    {        
        modelBuilder.HasOne(x => x.ProductDim)
                    .WithMany(x => (ICollection<TEntity>)x.LineBases)
                    .HasForeignKey("ProductDimId")
                    .IsRequired()
                    .OnDelete(DeleteBehavior.Restrict);       
    }
}

The configuration will be applied to the child models of my LineBase model:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);
    ...
    modelBuilder.ApplyConfiguration<SalesOrderLine>(new LineBaseConfiguration<SalesOrderLine>());
    modelBuilder.ApplyConfiguration<WmsJournalLine>(new LineBaseConfiguration<WmsJournalLine>());
    modelBuilder.ApplyConfiguration<PurchOrderLine>(new LineBaseConfiguration<PurchOrderLine>());
    ...
}

Now when I add the migration to my project the foreign key is generated as ReferentialAction.Cascade.

Migration output:

migrationBuilder.AddForeignKey(
    name: "FK_PurchOrderLine_ProductDim_ProductDimId",
    table: "PurchOrderLine",
    column: "ProductDimId",
    principalTable: "ProductDim",
    principalColumn: "Id",
    onDelete: ReferentialAction.Cascade);

migrationBuilder.AddForeignKey(
    name: "FK_SalesOrderLine_ProductDim_ProductDimId",
    table: "SalesOrderLine",
    column: "ProductDimId",
    principalTable: "ProductDim",
    principalColumn: "Id",
    onDelete: ReferentialAction.Cascade);

migrationBuilder.AddForeignKey(
    name: "FK_WmsJournalLine_ProductDim_ProductDimId",
    table: "WmsJournalLine",
    column: "ProductDimId",
    principalTable: "ProductDim",
    principalColumn: "Id",
    onDelete: ReferentialAction.Cascade);

Only if i remove the IsRequired() from my model configuration. The correct RefrentialAction is generated. But then I've made the foreign key property nullable, which I have to avoid.

In this case the migration generates multiple cascades paths. So it is wothless, due i can't apply it to my database.

So is there any other way to get my ForeignKey field was a non-nullable and a correct delete action?

1 Answers

I don't know why, but IsRequired add the foreign key in the entity. All modification do on the foreign key builder (like OnDelete) don't impact the foreign key because it is already build and added in entity. This sound like a bug.

The solution(hack?) is to call IsRequired at last :

modelBuilder.HasOne(x => x.ProductDim)
            .WithMany(x => (ICollection<TEntity>)x.LineBases)
            .HasForeignKey("ProductDimId")
            .OnDelete(DeleteBehavior.Restrict)
            .IsRequired(); // At last
Related