EF6 级联删除
- 级联删除跟数据库外键有关
- 跟外键字段的是否可空有关
- 数据库中删除主表后,从表的外键会被置为NULL,或者关联的从表记录一起被删除.
- 可空类型外键 配置时使用 HasOptional
- 必填类型外键 配置时使用 HasRequired
- HasOptional配置: 在删除主表时,从表的外键会被置为NULL,从表数据不会跟着被删除;
- WillCascadeOnDelete 默认是为false,但从表的外键字段必须是可空的,如 int? RoleId;
- HasRequired配置: 在删除主表时,从表的记录会被跟着删除;
- WillCascadeOnDelete默认是为true,但从表的外键字段必须是必填的,如 int RoleId;
- 避坑:如果主表配置了与子表关联的属性,如下面的ICollection< User > Users ,则单独删除主表记录(已加载子表,Users属性有记录)的时候会报从表外键缺失无法删除,这是因为在DbContext中,存在了从表的跟踪记录,这些跟踪记录的状态是Unchanged未改变的,所以DbContext认为你只删除主表记录而不删除从表,导致数据库报从表外键缺失而删除失败.
- 解决方法1,查询主表时不手动Include()从表 Users;
- 解决方法2,对于已经Include从表的,需要手动将从表记录的状态也设置为删除,role.Users.ToList()后再删除不然还是会报错;
- 解决方法3,数据库从表的外键设置为可空的(没有试,好像也不太优雅);
- 取消级联
- 一对多:modelBuilder.Conventions.Remove(new OneToManyCascadeDeleteConvention());
- 一对一:modelBuilder.Conventions.Remove(new OneToOneConstraintIntroductionConvention());
- 多对多:modelBuilder.Conventions.Remove(new ManyToManyCascadeDeleteConvention());
报错信息
The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable.
When a change is made to a relationship,
the related foreign-key property is set to a null value.
If the foreign-key does not support null values,
a new relationship must be defined,
the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted
操作失败:无法更改关系,因为一个或多个外键属性不可为空。
当对关系进行更改时,相关的外键属性将设置为空值。
如果外键不支持空值,则必须定义新的关系,必须为外键属性分配另一个非空值,或者必须删除不相关的对象
举例代码
主表
public partial class Role
{
public Role()
{
Users = new HashSet<User>();
}
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<User> Users { get; set; }
}
public class RoleConfiguration : EntityTypeConfiguration<Role>
{
public RoleConfiguration()
{
HasKey(x => x.Id);
}
}
从表
public partial class User
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public int RoleId { get; set; }
public virtual Role Role { get; set; }
}
public class UserConfiguration : EntityTypeConfiguration<User>
{
public UserConfiguration()
{
HasKey(x => x.Id);
HasRequired(m => m.Role)
.WithMany(m=>m.Users)
.HasForeignKey(x => x.RoleId)
.WillCascadeOnDelete(true);
}
}
EF Core 级联删除
与EF6应该是类似逻辑的,虽然没试过
EFCore 级联删除微软官方文档
- HasOne 对应必填
- OwnsOne 对应可空
- DeleteBehavior 删除选项可以参照微软官网文档
EF6 和EF Core 程序包管理器
- 当项目里面同时包含EF6和EFCore的控制台工具时,默认是启用EF Core来生成迁移的,
需要用EF6时,需要在命令前加前缀EntityFramework6\命令(只有我这种笨蛋会在EF6项目里面安装EFCore的工具包了).
EF Guid 顺序自增
新增时保持属性值为 Guid.Empty即可,保存后即可获得连续的Guid
在SqlServer中使用的是newsequentialid()方法获取,其原理感兴趣的可以去深究下.
Property(x => x.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
没有主键的表(不是视图)
- EF并不检查class类的Key是否与数据库表的Key的对应关系,所以实体类可以自定义你认为不重复的字段作为Key.
- 如果自定义Key的字段在数据库表中可能存在为null的情况,可以试着用AsNoTracking()查询.
- 自定义Key字段虽然在数据库表中或许不是必填,但在程序中一定为必填,且不能重复.
- 满足以上条件,这个实体也可以正常使用增删改查功能了.虽然在代码层面解决Key的问题,但是数据库表的问题还是存在,比如Key重复会造成EF报错.