带触发器的DbContext.SaveChanges

时间:2019-09-06 10:42:18

标签: entity-framework-6

我想使用实体框架(而不是ADO.NET)访问我的数据库,但是我有两个问题:

  1. 如何使DbContext.SaveChanges返回已由SQL触发器更新的字段?

我在数据库中的所有表都具有TraceVersion(int)和UTimestamp(datetime)字段。我有一个AFTER UPDATE触发器,该触发器将TraceVersion递增1并将UTimestamp设置为当前日期和时间。不幸的是,DbContext.SaveChanges不会检索由触发器更新的字段,因此我的实体与数据库的值不同。有没有一种方法可以指示DbContext.SaveChanges保存到数据库后检索字段TraceVersion和UTimestamp?

  1. 如何使DbContext.SaveChanges发送UPDATE语句,将字段Deleted设置为1,而不是发送DELETE语句?

我在数据库中的所有表都具有“已删除(位)”字段。如果字段Deleted = 1,则AFTER UPDATE触发器将删除记录,并将该记录插入审核表中。

对于这两个问题,以下是带有触发器的表的示例:

CREATE TABLE [dbo].[CUsers](
    [Id] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY,
    [NTUser] [varchar](50) NULL,
    [FName] [varchar](20) NULL,
    [LName] [varchar](50) NULL,
    [Active] [bit] NULL,
    [Deleted] [bit] NOT NULL,
    [TraceVersion] [int] NOT NULL,
    [UTimeStamp] [datetime] NOT NULL,
 CONSTRAINT [IX_CUsers] UNIQUE NONCLUSTERED ([NTUser] ASC) ON [PRIMARY]
) ON [PRIMARY]
GO

CREATE TABLE [dbo].[CUsers_Audit](
    [Id] [int] NOT NULL,
    [NTUser] [varchar](50) NULL,
    [FName] [varchar](20) NULL,
    [LName] [varchar](50) NULL,
    [Active] [bit] NULL,
    [Deleted] [bit] NOT NULL,
    [TraceVersion] [int] NOT NULL,
    [UTimeStamp] [datetime] NOT NULL,
 CONSTRAINT [PK_CUsers_Audit] PRIMARY KEY CLUSTERED ([Id] ASC, [TraceVersion] ASC) ON [PRIMARY]) ON [PRIMARY]
GO

---------- AUDIT TRIGGER SCRIPT FOR TABLE CUsers---------------
CREATE TRIGGER [dbo].[trCUsers_AUDIT_UD] ON [dbo].[CUsers]
AFTER UPDATE, DELETE
AS
/* If no rows were affected, do nothing */
IF @@ROWCOUNT=0
    RETURN

SET NOCOUNT ON
BEGIN TRY
    DECLARE @Counter INT, @Now DATETIME
    SET @Now = GETDATE()
    /* Check the action (UPDATE or DELETE) */
    SELECT @Counter = COUNT(*)
    FROM INSERTED
    IF @Counter = 0 --> DELETE
        THROW 50000, 'DELETE action is prohibited for CUsers', 1

    /* Insert previous record to Audit */
    INSERT INTO CUsers_Audit([Id],[NTUser],[FName],[LName],[Active],[Deleted],[TraceVersion],[UTimeStamp])  
    SELECT d.[Id],d.[NTUser],d.[FName],d.[LName],d.[Active],d.[Deleted],d.[TraceVersion],d.[UTimeStamp]
    FROM DELETED d

    /* Update master record TraceVersion, UTimeStamp */
    UPDATE main
    SET main.TraceVersion = d.TraceVersion + 1, main.UTimeStamp = @Now
    FROM CUsers main
    INNER JOIN DELETED d ON d.Id = main.Id
    INNER JOIN INSERTED i ON i.Id = main.Id

    /* Process deleted rows */
    IF NOT EXISTS(SELECT 1 FROM INSERTED WHERE Deleted = 1)
        RETURN
    /* Re-insert last updated master record into Audit table where Deleted = 1 */
    INSERT INTO CUsers_Audit([Id],[NTUser],[FName],[LName],[Active],[Deleted],[TraceVersion],[UTimeStamp])  
    SELECT d.[Id],d.[NTUser],d.[FName],d.[LName],d.[Active],d.[Deleted],d.[TraceVersion],d.[UTimeStamp]
    FROM CUsers d
    INNER JOIN INSERTED i ON d.Id = i.Id
    WHERE i.Deleted = 1

    /* Delete master record */
    DELETE c
    FROM CUsers c
    INNER JOIN INSERTED i ON c.Id = i.Id
    WHERE i.Deleted = 1
END TRY
BEGIN CATCH
    THROW
END CATCH
GO

ALTER TABLE [dbo].[CUsers] ENABLE TRIGGER [trCUsers_AUDIT_UD]
GO

对于第一个问题,我知道我可以覆盖SaveChanges并按照以下代码进行重新加载:

public override int SaveChanges()
    {
        var entriesModified = 
            ChangeTracker.Entries().Where(
                e => e.State == EntityState.Modified).ToList();
        int rowCount = base.SaveChanges();
        if (rowCount > 0)
        {
            entriesModified.ForEach(e=>e.Reload());
        return rowCount;
    }

但是这将重新加载整个记录,这很耗时。我只想重新加载TraceVersion和UTimeStamp。此外,此重新加载发生在base.SaveChanges事务之外,因此TraceVersion和UTimeStamp可能在两者之间发生了更改。

我希望SaveChanges进行更新,但是还要在同一事务中发出以下SQL语句:

SELECT TraceVersion, UTimeStamp FROM CUsers WHERE Id=@Id

这可能吗?

0 个答案:

没有答案