【问题标题】:Is there a way to trigger a stored procedure at the end of all the insert/delete on an entity有没有办法在实体上的所有插入/删除结束时触发存储过程
【发布时间】:2020-12-22 11:09:39
【问题描述】:

我的应用程序正在使用实体框架,并且有一个名为 t_details 的表包含多条记录。保存应用程序后,此实体将被删除并添加。

我在这个表上还有一个 sql Db 触发器,当在这个表上发生插入/删除时,它会被执行。由于entity是一个一个地删除每个entry,一个一个地插入,所以每次delete或insert都会触发Db trigger,性能变慢。

有没有办法我可以在所有删除后启动触发器一次,在所有插入结束时启动一次触发器。

触发器现在创建如下

Create TRIGGER test.trigger_Data
   ON  test.t_details
   AFTER INSERT,DELETE,UPDATE
AS 
BEGIN

    EXEC [test].[spu_call_details]  -- this in turn will call some views/tables etc.
END

【问题讨论】:

  • 请用您正在使用的适当数据库标记您的问题。您需要statement level trigger,但答案取决于您使用的数据库。
  • @Popeye:数据库是sql server
  • 不是说我有一个解决方案,但出于好奇,数据库如何知道哪个删除/插入是最后一个?
  • @500-InternalServerError :是的,那是我有点卡住了。
  • 您从错误的角度看待这个“问题”。您的应用程序代码执行一项任务(应用程序被保存?),它只有它具有上下文 - 不是您的触发器,也不是数据库引擎。您的代码属于此“事件”。

标签: c# sql sql-server entity-framework triggers


【解决方案1】:

我认为如果不修改应用程序,您将无法做自己想做的事。在 SQL Server 中,触发器是 语句 级别的。

这里有一些选项:

  • 更改应用程序以在单个语句中执行所有删除操作。然后触发器将为事务执行一次。
  • 更改应用程序以指示批处理的结束——也许在某些表上进行更新以指示最后一次。然后将其用作触发器。
  • 您可以使用事件队列玩游戏。您可以安排它运行在一分钟内,而不是运行代码。如果在该时间范围内发生了另一次删除,则不要运行代码。

【讨论】:

    【解决方案2】:

    如果您只关心在应用程序添加/更新/删除记录时对此进行跟踪,那么您可以利用有界上下文在适用的 DbContext 的 SaveChanges 上执行此操作:

    首先创建一个单独的 DbContext,该 DbContext 将为该存储过程调用提供服务或与所需实体交互以跟踪更改记录。创建对此 DbContext 的引用,作为您要跟踪实体更新的应用程序 DbContext 的一部分。

    举个例子,一个 LoggingDbContext 和一个 AppDbContext:

    public class LoggingDbContext : DbContext
    {
      // ...
    }
    
    public class AppDbContext : DbContext
    {
        private readonly LoggingDbContext _loggingDbContext = null;
        public AppDbContext(string connectionString, LoggingDbContext loggingDbContext)
            : base(connectionString)
        {
            _loggingDbContext = loggingDbContext ?? throw new ArgumentNullException("loggingDbContext");
        }
    
        public override int SaveChanges()
        {
            // Do your logging call...
            var log = new Log { Message = "SaveChanges Called!" };
            _loggingDbContext.Logs.Add(log);
            _loggingDbContext.SaveChanges();
    
            return base.SaveChanges();
        }
    }
    

    OnSaveChanges 覆盖可以调用您的存储过程或任何必要的。如果您想区分何时添加、删除实体等,您可以使用 ChangeTracker 检查特定更改类型或特定实体的更改:

    var updatedCustomers = ChangeTracker.Entries()
        .Where(x => x.State == EntityState.Modified)
        .Select(x => x.Entity)
        .OfType<Customer>();
    
    var insertedCustomers = ChangeTracker.Entries()
        .Where(x => x.State == EntityState.Added)
        .Select(x => x.Entity)
        .OfType<Customer>();
    
    var deletedCustomers = ChangeTracker.Entries()
        .Where(x => x.State == EntityState.Deleted)
        .Select(x => x.Entity)
        .OfType<Customer>();
    

    从那里您可以检查带有 .ToList().Select() 的实体以了解特定详细信息,或者只需执行 .Any().Count() 以评估是否添加/更新/删除了特定实体类型等。

    我在我的 DbContexts 中将此模式用于跟踪 LastModifiedBy 用户/日期跟踪之类的模型。我有一个 CurrentUserLocator 用于解析会话的当前登录用户,然后是一个包含 CreatedBy/CreatedAt/LastModifiedBy/LastModifiedAt 字段的“可编辑”实体的基类。这样,当我的 DbContext 保存详细信息时,上下文 SaveChanges 看起来像这样:

    // These DbContexts do not allow for hard Deletes. 
    if (ChangeTracker.Entries().Any(x => x.State == EntityState.Deleted))
        throw new ApplicationException("Hard deletes are not supported.");
    
    var currentUser = Users.Single(x => x.UserId == CurrentUserLocator.CurrentUserId); 
    var updatedEntities = ChangeTracker.Entries()
        .Where(x => x.State == EntityState.Modified)
        .Select(x => x.Entity)
        .Cast<EditableEntityBase>();
    foreach (var entity in updatedEntities)
    {
        entity.LastModifiedBy = currentUser;
        entity.LastModifiedAt = DateTime.Now;
    }
    
    var insertedEntities = ChangeTracker.Entries()
        .Where(x => x.State == EntityState.Added)
        .Select(x => x.Entity)
        .Cast<EditableEntityBase>();
    foreach (var entity in insertedEntities)
    {
        entity.CreatedBy = entity.LastModifiedBy = currentUser;
        entity.CreatedAt = entity.LastModifiedAt = DateTime.Now;
    }
    
    return base.SaveChanges();
    

    上下文还跟踪不可编辑的实体(查找等),因此这也有助于防止以某种方式插入或更新不可编辑的实体的情况。由于我的系统在此示例中使用软删除,因此它还可以防止硬删除实体。此方法可用于利用单独的 DbContext 调用存储过程或以其他方式修改其他实体等,而不会“绊倒”SaveChanges 中的那些更改跟踪挂钩。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-01-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多