2013-04-10 85 views
2

在我的SQL表(2008)中,我有一個名爲Timestamp的時間戳列。EF併發導致觸發問題

在我的.NET項目,我有一個POCO類的屬性

public byte[] Timestamp {get; set;} 

在我的配置代碼,我有以下幾點:

Property(p => p.Timestamp).IsRowVersion(); 

現在,如果我打開兩個編輯畫面對其中一個進行更改並保存(保存被接受),然後在第二個中進行更改並保存(保存被DbUpdateConcurrencyException拒絕)。

有一件我覺得奇怪的事情是,即使當我收到併發異常時,SQL事件探查器也會顯示發送到SQL數據庫的更新請求。更新不會在數據庫上提交,但會被髮送。這是正常的嗎?我有預期的EF提前檢查,甚至不發送請求。

最後,如果我能夠在這個表中的觸發器:

ALTER TRIGGER [dbo].[trg_person_log_changes] 
    ON [dbo].[Person] 
    AFTER INSERT,DELETE,UPDATE 
AS 
BEGIN 
-- SET NOCOUNT ON added to prevent extra result sets from 
-- interfering with SELECT statements. 
SET NOCOUNT ON; 
DECLARE @auditBody XML 
DECLARE @actionType char(1) 
DECLARE @username nvarchar(100) 

if not exists(select * from deleted) 
    Begin 
     SET @actionType = 'I' 
     SET @username = (select Case 
            When lastchangedby is null or lastchangedby = '' then Suser_name() 
            Else lastchangedby 
           End 
         from inserted) 
    End 
else if not exists(select * from inserted) 
    Begin 
     SET @actionType = 'D' 
     SET @username = Suser_name() 
    End 
else 
    Begin 
     SET @actionType = 'U' 
     SET @username = (select distinct Case 
            When lastchangedby is null or lastchangedby = '' then Suser_name() 
            Else lastchangedby 
           End 
         from inserted) 
    End 

If @actionType = 'I' 
    Set @auditBody = (select 'Person' as "@tableName", 'True' as "@synch", 
     (select * from inserted for xml path('DataItem'), type, binary base64) 
     for xml path('Root')) 
Else 
    SET @auditBody = (select 'Person' as "@tableName", 'True' as "@synch", 
     (select * from deleted for xml path('DataItem'), type, binary base64) 
     for xml path('Root')) 

insert into [dbo].[AuditLog] 
      ([Action] 
      ,[ActionDate] 
      ,[ActionUser] 
      ,[AuditData] 
      ) 
    values (
      @actionType 
      ,getdate() 
      ,@username 
      ,@auditBody) 
END 

現在,當我嘗試保存第二個編輯,我不再得到DbUpdateConcurrencyException,我得到一個錯誤,指出ActionUser不能空值。再一次,我認爲我的觸發器正在執行,因爲即使存在衝突,EF也允許更新通過。

關於我可能做錯什麼想法?

注意

這是一個MVC項目。在我的編輯POST控制器中,我收到一個DTO對象,其中包含所有表單屬性(其中之一是Timestamp - 我的編輯表單上的隱藏字段)。我正在從datacontext加載編輯後的人員,並將編輯後的DTO對象的屬性映射到從datacontext返回的Domain對象中。

回答

2

執行樂觀併發權的唯一方法是進行版本檢查,並確保在實際更新完成之前沒有任何更改。最簡便的方法是在UPDATE聲明中加入版本檢查,這就是EF所做的。所以它發送一個更新語句,它看起來像

UPDATE table SET columnA = value 
WHERE rowversion = xxxx 

然而,當語句沒有找到與rowversion它在它返回的存儲與預期不同的數量受影響的行和例外是拋出行。 並且交易回滾。所以是的,你會在SQL Profiler中看到一條語句,但它從不承諾。

該觸發器作爲更新語句的一部分運行,即在向EF回報有關更新命令之前。顯然,@username在進程中沒有得到值,所以這會引發一個SQL異常,在發現任何併發衝突之前破壞更新方。

+0

謝謝你。我的一個朋友(謝謝傑克)給了我一個解決方案。他告訴我在我的觸發器中檢查@@ ROWCOUNT = 0。如果這是真的,那麼我只是回來,並沒有做任何添加到審計日誌的邏輯。 – RHarris 2013-04-10 20:26:20

+0

好的,可以節省一些性能,但是日誌的添加應該回滾以及達到目的。 – 2013-04-10 20:28:01

+0

對不起,應該更清楚了。沒有添加到日誌回滾。由於更新沒有更新任何行(由於where子句),因此觸發器中的@@ ROWCOUNT爲0 ...因此,我退出觸發器並且不向審計日誌添加任何內容......沒有任何事情可以回滾! – RHarris 2013-04-10 21:10:16