【问题标题】:Delete all related rows to user using EF .NET CORE使用 EF .NET CORE 删除所有与用户相关的行
【发布时间】:2021-02-27 11:08:22
【问题描述】:

我对数据删除没什么问题。让我们假设我必须删除用户和该用户的所有相关行。我用 c# 写了一些代码,但是需要很长时间才能完成。这是我的一些代码。我的问题是如何以最快的方式将所有相关数据删除给用户。我想到了数据库上的一些触发器,好不好?

_context.ChangeTracker.LazyLoadingEnabled = false;
        _context.ChangeTracker.AutoDetectChangesEnabled = false;

        IQueryable<Domain.Entities.Identity.User> customObjectQueryable = _context.Users.Where(x => x.Id == request.UserId);

        var firstQuery = customObjectQueryable
            .Include(x => x.NotificationsCreated)
            .Include(x => x.UsersMeetings)
            .Include(x => x.UserVisibility)
            .Include(x => x.UsersGroups)
            .Include(x => x.Roles)
            .Include(x => x.RefreshTokens)
            .Include(x => x.Groups)
            .Include(x => x.Notifications)
            .Include(x => x.Meetings)
            .AsNoTracking();

【问题讨论】:

    标签: sql .net linq triggers


    【解决方案1】:

    如果您使用的是 EF Core 5,如果您添加 AsSplitQuery(),它可以加快您的查询速度。

    另一种解决方案是单独加载集合。不要禁用更改跟踪器。

    var customObjectQueryable = _context.Users.Where(x => x.Id == request.UserId);
    
    var entity = customObjectQueryable
            .Include(x => x.NotificationsCreated)
            .Include(x => x.UserVisibility)
            .First();
    
    var entry = _context.Entry(entity);
    
    entry.Collection(x => x => x.UsersMeetings).Load();
    entry.Collection(x => x.UsersGroups).Load();
    entry.Collection(x => x.Roles).Load();
    entry.Collection(x => x.RefreshTokens).Load();
    entry.Collection(x => x.Groups).Load();
    entry.Collection(x => x.Notifications).Load();
    entry.Collection(x => x.Meetings).Load();
    
    entity.UsersMeetings.Clear();
    entity.UsersGroups.Clear();
    entity.Roles.Clear();
    entity.RefreshTokens.Clear();
    entity.Group.Clear();
    entity.Notifications.Clear();
    entity.Meetings.Clear();
    
    _context.SaveChanges();
    

    您还可以将模型配置为支持级联删除。按照这个链接如何配置https://docs.microsoft.com/en-us/ef/core/saving/cascade-delete

    【讨论】:

      【解决方案2】:

      您在删除用户时依赖CascadeOnDelete。这是一个相对较慢的过程,因为对于要删除的每个用户,您的数据库管理系统都必须检查所有相关表以查看与要删除的用户是否存在关系。

      先删除相关对象再删除客户会更快:

      using(var dbContext =  new MyDbContext(...))
      {
          Customer userToDelete = dbContext.Users.Where(...).FirstOrDefault();
      
          // before deleting this user, remove all related items
          var rolesToRemove = dbContext.Roles.Where(role => role.UserId == userToDelete.Id);
          dbContext.Roles.RemoveRange(rolesToRemove.ToList());
      
          var notificationsToRemove = dbContext.Notifications
              .Where(notification => notification.UserId == userToDelete.Id);
          dbContext.Notifications.RemoveRange(notificationsToRemove.ToList());
      
          ... // etc.
          dbContext.User.Remove(userToDelete();
          dbContext.SaveChanges();
      }
      

      Entity Framework 的问题是,您需要先获取项目,然后才能删除它们。

      如果您需要经常删除用户,则可以通过创建存储过程来绕过此提取。

      class MyDbContext : DbContext
      {
          public DbSet<User> Users {get; set;}
          ... // etc
      
          protected override OnModelCreating(...)
          {
               ... // fluent API, table names, column names, relations between tables, ...
      
               this.CreateStoredProcedureRemoveUser()
          }
      
          private void CreateStoredProcedureUpdateUsageCosts(DemoContext context)
          {
              const string sqlText = @"Create Procedure RemoveUser @UserId int as
              Begin
                 ... // Sql code to remove UserMeetings, Roles, Groups, etc
                 ... // Sql code to remove the user
              End";
      
              this.Database.ExecuteSqlComment(sqlText);
          }
      
          // Procedure to remove the user:
          public void RemoveUser(int userId)
          {
              const string sqlCommandRemoveUser= @"Exec RemoveUser @UserId";
              object[] commandParameters = new object[]
              {
                  new SqlParameter(@"@UserId", userId),
              };
              this.Database.ExecuteSqlCommand(sqlCommandRemoveUser, commandParameters);
      
          }
      }
      

      用法:

      int userId = ...
      using (var dbContext = new MyDbContext())
      {
          dbContext.RemoveUser(userId);
      }
      

      小心:因为过程是立即执行的,所以一旦调用了方法就不能返回。例如,如果您想要回退,在删除角色后删除组时遇到异常,请使用 DbContext.Database.BeginTranscation / EndTransaction,以恢复原始情况。

      【讨论】:

        猜你喜欢
        • 2021-04-06
        • 1970-01-01
        • 2019-11-17
        • 2012-08-14
        • 2018-11-12
        • 2021-10-06
        • 2018-07-19
        • 2019-03-29
        相关资源
        最近更新 更多