2009-10-01 30 views
0

在創建用戶時,必須將一行插入用戶和電子郵件表中。它們都可能會失敗(唯一的約束)。我怎樣才能找出哪個是失敗的原因?我的想法是在插入或解析返回的SqlException(我不希望這樣做)之前使用鎖和查詢數據庫。什麼是從級聯保存中檢查錯誤的正確方法?

編輯:我應該提到這將在幾臺機器上同時運行,並且我希望它支持不同的數據庫。

編輯2:我的解決方案最終使用鎖來檢查重複項。存儲過程是一種選擇,但我不想將業務邏輯放入數據庫中。我曾爲其他人說過我意識到網絡農場的競爭狀況,但這種情況的罕見情況並不值得進一步研究。

回答

2

應該使用異常處理來捕獲非主要的情況,例如數據庫關閉或命令超時。如果你對用戶是唯一的,並且電子郵件是唯一的,你應該在你提交數據之前測試它們。依靠檢查/索引約束作爲處理這些情況的一種方式將在長期運行中造成混亂。此外,錯誤處理中的一個關鍵最佳實踐是永遠不要讓最終用戶知道錯誤發生的原因。

+0

這種情況可能是故障需要傳遞給服務類進行日誌記錄的原因(不是我的情況,只是一個例子) – 2009-10-01 18:25:11

0

你應該在業務邏輯的某個地方捕捉那種情況,而不是單純依靠數據庫來給你找出你想要的錯誤。

+0

如果代碼在多臺機器上運行,一臺機器檢查並在它可以保存另一臺機器之前會保存該電子郵件,會發生什麼情況? – 2009-10-01 18:19:07

+0

您需要通過回滾提交來找到彌補方法。 – 2009-10-01 18:21:22

+0

它會回滾,但在那種情況下,我不知道回滾的原因。 – 2009-10-01 18:23:17

1

使用存儲過程,並檢查已知條件,這將使交易失敗的事務中,如:

BEGIN TRANSACTION 
IF EXISTS (SELECT UserID FROM User WHERE UserID = @UserID) 
    BEGIN 
     ROLLBACK 
     SELECT 'User already exists in the User table.' 
     RETURN 1 
    END 

IF EXISTS (SELECT UserID FROM Email WHERE UserID = @UserID) 
    BEGIN 
     ROLLBACK 
     SELECT 'User already exists in the Email table.' 
     RETURN 2 
    END 

INSERT INTO User ... 
INSERT INTO Email ... 
COMMIT 
RETURN 0 

這實際上是利用兩種機制返回一個錯誤(返回代碼和結果集) ;它通常只有使用一個纔有意義。

0

我不會依賴表約束進行數據驗證。在插入之前用查詢驗證數據。例外是要創建的昂貴對象。另外,我更喜歡有限制來防止無效數據,但不能驗證。我想到一個約束作爲桌子的安全帶。只有在發生錯誤時才應該調用它。業務邏輯應在插入前驗證所有數據。如果您的目標數據庫不支持它們,請不要依賴存儲過程。

這是我處理它的一般方法。

 private void Form1_Load(object sender, EventArgs e) 
    { 
     OleDbConnection conn = null; 
     OleDbTransaction t = null; 
     try 
     { 
      conn = new OleDbConnection("a database"); 

      conn.Open(); 

      //query both tables to prevent insert fail, 
      //obviously UserID should be parameter. 
      var cmd = new OleDbCommand("select count(*) from User where UserID = 1", conn); 
      var count = (double)cmd.ExecuteScalar(); 

      cmd.CommandText = "select count(*) from Email where UserID = 1"; 
      count += (double)cmd.ExecuteScalar(); 

      if (count != 0) 
      { 
       MessageBox.Show("Record exists"); 
       return; 
      } 

      t = conn.BeginTransaction(); 

      //insert logic goes here 


      t.Commit(); 
     } 
     catch (Exception x) 
     { 
      //we still need catch block, someone else may have updated the 
      //data after you checked but before you insert or db open may 
      //fail 

      MessageBox.Show(x.Message); 
      if (t != null) 
       t.Rollback(); 
     } 
     finally 
     {     
      if (conn != null) 
       conn.Close(); 
     } 
    } 
相關問題