我有三个带有代码优先模型的简单类。
我正在使用来自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语句)....有谁知道为什么?
答案 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);