Question

I have three simple classes with a code first model.

I'm using migration's 'update-database' command from visual-studio to generate an empty new database.

This is working fine with EF5 (database is created with the configured WillCascadeOnDelete(false) relation).

But as soon as I upgrade the project to use EF6 the command 'update-database' fail with a SQL circular exception (I always start without a database).

Apparently the fluent api on OnModelCreating is ignored. The sql constraint is created with ON DELETE CASCADE and fail (this does't appen in EF5 where the constraint is created correctly).

public class LizardModel : DbContext
{
    public LizardModel()
    {

    }
    public LizardModel(string nameOrConnectionString)
        : base(nameOrConnectionString)
    {

    }

    public DbSet<Account> Accounts { get; set; }
    public DbSet<Area> Areas { get; set; }
    public DbSet<AreaPermission> AreaPermissions { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<Area>()
                    .HasRequired<Account>(i => i.Owner)
                    .WithMany(a => a.Areas)
                    .HasForeignKey(d => d.OwnerId)
                    .WillCascadeOnDelete(false);
    }

}

The three class represent a very common pattern (in my opinion):

public class Area
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid Id { get; set; }

    [InverseProperty("Areas"), ForeignKey("OwnerId")]
    public virtual Account Owner { get; set; }
    public Guid OwnerId { get; set; }

    [InverseProperty("Area")]
    public virtual ICollection<AreaPermission> AreaPermissions { get; set; }

    [Timestamp]
    public byte[] RowVersion { get; set; }
}


public class Account
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid Id { get; set; }

    [Required, MaxLength(100)]
    public string Name { get; set; }

    public bool IsAdmin { get; set; }

    [Timestamp]
    public byte[] RowVersion { get; set; }

    [InverseProperty("Owner")]
    public virtual ICollection<Area> Areas { get; set; }

    [InverseProperty("Account")]
    public virtual ICollection<AreaPermission> AreaPermissions { get; set; }
}

public class AreaPermission
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid Id { get; set; }

    [InverseProperty("AreaPermissions"), ForeignKey("AreaId")]
    public virtual Area Area { get; set; }
    public Guid AreaId { get; set; }

    [InverseProperty("AreaPermissions"), ForeignKey("AccountId")]
    public virtual Account Account { get; set; }
    public Guid AccountId { get; set; }

    [MaxLength(10)]
    public string Role { get; set; }

    [Timestamp]
    public byte[] RowVersion { get; set; }
}

I'm locked with this problem, any help or suggestions ? What does has changed from EF5 to EF6 ?

SQL code generated by EF5:

ALTER TABLE [dbo].[Areas] ADD CONSTRAINT [FK_dbo.Areas_dbo.Accounts_OwnerId] FOREIGN KEY ([OwnerId]) REFERENCES [dbo].[Accounts] ([Id])
ALTER TABLE [dbo].[AreaPermissions] ADD CONSTRAINT [FK_dbo.AreaPermissions_dbo.Areas_AreaId] FOREIGN KEY ([AreaId]) REFERENCES [dbo].[Areas] ([Id]) ON DELETE CASCADE
ALTER TABLE [dbo].[AreaPermissions] ADD CONSTRAINT [FK_dbo.AreaPermissions_dbo.Accounts_AccountId] FOREIGN KEY ([AccountId]) REFERENCES [dbo].[Accounts] ([Id]) ON DELETE CASCADE

Please note that the first constraint is NOT 'on delete cascade' ...

The same program, with EF6 updated via nuget (after deleting the database and issued a new 'update-database' command), generate all three constraint with the 'on delete cascade' attribute and this result in a SQL exception (completing ignoring my modelBuilder statement).... does anyone know why ?

Was it helpful?

Solution

This is bug in EF but workaround is fairly simple - you can reverse the order you declared the relationship in your OnModelCreating like so:

modelBuilder.Entity<Account>()
.HasMany<Area>(i => i.Areas)
.WithRequired(a => a.Owner)
.HasForeignKey(d => d.OwnerId)
.WillCascadeOnDelete(false);

Alternatively you can remove

[InverseProperty("Areas"), ForeignKey("OwnerId")]

on Area entity and

[InverseProperty("Owner")] 

on Account entity.

Bug seems to be with a code that merges the relationship declaration (you declare it twice).

Also, since your Account - Area relationship is 1-Many (required) you might get into trouble/inconsistent state, when you delete the principal, since the dependent entities wont be deleted. Consider making it 0..1 - Many (make OwnerId nullable, and change fluent API declaration to:

modelBuilder.Entity<Account>()
.HasMany<Area>(i => i.Areas)
.WithOptional(a => a.Owner)
.HasForeignKey(d => d.OwnerId)
.WillCascadeOnDelete(false);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top