当前位置: 首页 > 工具软件 > ef6 > 使用案例 >

C# EF 6 Core 避坑杂谈

东门宜
2023-12-01

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报错.
 类似资料: