EF Core之全局删除方案

EF Core之全局删除方案

本文主要对EF Core的级联删除和软删除的全局配置作说明

  • EF Core的外键默认是级联删除的,如果不想这样就只能手动配置,这里提供一种全局配置的方式:

    • DBContext的OnModelCreating就是我们要处理的地方:

      protected override void OnModelCreating(ModelBuilder modelBuilder)
      {
          var cascadeFKs = modelBuilder.Model.GetEntityTypes()
                                       .SelectMany(t => t.GetForeignKeys())
                                       .Where(fk => !fk.IsOwnership && fk.DeleteBehavior == DeleteBehavior.Cascade);
          foreach (var fk in cascadeFKs)
          {
              fk.DeleteBehavior = DeleteBehavior.Restrict;
          }
      }
      
  • 很多情况下我们需要软删除的方案,EF Core启用软删除也是很方便的:

    • 这里提供一种全局配置的方案:

      public const string _isDeletedProperty = "IsDeleted";
      
      private static readonly MethodInfo _propertyMethod = typeof(EF).GetMethod(nameof(EF.Property), BindingFlags.Static | BindingFlags.Public).MakeGenericMethod(typeof(bool));
      
      private static LambdaExpression GetIsDeletedRestriction(Type type)
      {
          var parm = Expression.Parameter(type, "it");
          var prop = Expression.Call(_propertyMethod, parm, Expression.Constant(_isDeletedProperty));
          var condition = Expression.MakeBinary(ExpressionType.Equal, prop, Expression.Constant(false));
          var lambda = Expression.Lambda(condition, parm);
          return lambda;
      }
      
      protected override void OnModelCreating(ModelBuilder modelBuilder)
      {
          foreach (var entity in modelBuilder.Model.GetEntityTypes())
          {
              //为需要软删除的表增加一个“IsDeleted”的字段,这里可以是bool类型的
              //配合EF Core的QueryFilter就可以实现软删除,也可以手动IgnoreQueryFilter来查询数据
              if (typeof(ISoftDeletable).IsAssignableFrom(entity.ClrType) == true)
              {
                  entity.AddProperty(_isDeletedProperty, typeof(bool));
      
                  modelBuilder
                      .Entity(entity.ClrType)
                      .HasQueryFilter(GetIsDeletedRestriction(entity.ClrType));
              }
          }
      }
      
      public override int SaveChanges(bool acceptAllChangesOnSuccess)
      {
          OnBeforeSaving();
          return base.SaveChanges(acceptAllChangesOnSuccess);
      }
      
      //SaveChanges的时候针对删除操作做特殊处理
      private void OnBeforeSaving()
      {
          foreach (var entry in ChangeTracker.Entries<ISoftDeletable>())
          {
              switch (entry.State)
              {
                  case EntityState.Added:
                      entry.CurrentValues[_isDeletedProperty] = false;
                      break;
      
                  case EntityState.Deleted:
                      entry.State = EntityState.Modified;
                      entry.CurrentValues[_isDeletedProperty] = true;
                      break;
              }
          }
      }
      
      //软删除的接口,用于标识,需要的表实现该接口即可
      public interface ISoftDeletable
      {
      }
      
    • 备注:以上方案不适用于有唯一索引的情况,如果要配合唯一索引,可以将IsDeleted字段设置为int或者string类型,UniqueColumn + IsDeleted组成聚合索引。删除的时候IsDeleted字段要变成一个唯一的值。

posted @ 2019-03-16 07:17  shadowxs  阅读(322)  评论(0编辑  收藏  举报