实体框架代码首先在EF5中工作,但在EF6中不工作

时间:2014-03-07 16:31:07

标签: c# entity-framework ef-code-first ef-migrations

我有三个带有代码优先模型的简单类。

我正在使用来自visual-studio的migration的'update-database'命令来生成一个空的新数据库。

这适用于EF5(数据库是使用已配置的WillCascadeOnDelete(false)关系创建的。)

但是一旦我升级项目以使用EF6,命令'update-database'就会失败并出现SQL循环异常(我总是在没有数据库的情况下启动)。

显然OnModelCreating上的流畅api被忽略了。使用ON DELETE CASCADE创建sql约束并失败(这不会在EF5中正确创建约束)。

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);
    }

}

这三个类代表了一种非常常见的模式(在我看来):

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; }
}

我已经解决了这个问题,有什么帮助或建议吗?从EF5到EF6有什么变化?

EF5生成的SQL代码:

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

请注意第一个约束是'删除级联' ...

同样的程序,通过nuget更新EF6(删除数据库并发布新的'update-database'命令后),使用'on delete cascade'属性生成所有三个约束导致SQL异常(完成忽略我的modelBuilder语句)....有谁知道为什么?

1 个答案:

答案 0 :(得分:0)

这是EF中的错误,但解决方法相当简单 - 您可以在OnModelCreating中反转您声明关系的顺序,如下所示:

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

或者你可以删除

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

在区域实体和

[InverseProperty("Owner")] 

在帐户实体上。

Bug似乎与合并关系声明的代码(你声明它两次)。

此外,由于您的帐户 - 区域关系是1-Many(必需),当您删除主体时,您可能会遇到麻烦/不一致状态,因为依赖实体不会被删除。考虑将其设为0..1 - 许多(使OwnerId可以为空,并将流畅的API声明更改为:

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