2012-06-28 163 views
4

我想在我的WinForms應用程序中實現N層體系結構,以便從數據訪問中分離(只是邏輯上 - 在一個項目中)業務邏輯,但是我對使用BLL中的transacion存在一些疑問。我在互聯網上找到的所有教程都是該體系結構的非常簡單的實現(沒有事務),或者對我的需求來說太複雜。試圖找到我自己的方式,我已經到了這個地步,我不知道處理BLL層交易的最佳方式。
我會嘗試使用一些簡單的例子來說明這個問題(所有的類都是在單獨的文件):處理N層體系結構事務

//DTO - Data Transfer Objects 
public class Item 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

public class SomeOtherItem 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

//DAL - Data Access layer 
public class ItemDAL 
{ 
    public ItemDAL() 
    { 
    } 

    public void Add(Item item) 
    { 
     using (NpgsqlConnection conn = new NpgsqlConnection(connString)) 
     { 
      conn.Open(); 
      using (NpgsqlCommand cmd = new NpgsqlCommand()) 
      { 
       cmd.Connection = conn; 
       cmd.CommandText = @"INSERT INTO tbl_items (name) 
            VALUES (@name)"; 
       cmd.Parameters.AddWithValue("@name", item.Name); 
       cmd.ExecuteNonQuery(); 
      } 
     } 
    } 
} 

public class SomeOtherItemDAL 
{ 
    public SomeOtherItemDAL() 
    { 
    } 

    public void Add(SomeOtherItem someOtherItem) 
    { 
     using (NpgsqlConnection conn = new NpgsqlConnection(connString)) 
     { 
      conn.Open(); 
      using (NpgsqlCommand cmd = new NpgsqlCommand()) 
      { 
       cmd.Connection = conn; 
       cmd.CommandText = @"INSERT INTO tbl_some_other_items (name) 
            VALUES (@name)"; 
       cmd.Parameters.AddWithValue("@name", someOtherItem.Name); 
       cmd.ExecuteNonQuery(); 
      } 
     } 
    } 
} 

//BLL - Business Logic Layer 
public class SomeBLL 
{ 
    public SomeBLL() 
    { 
    } 

    public void Add(Item item, SomeOtherItem someOtherItem) 
    { 

     ItemDAL itemDAL = new ItemDAL(); 
     SomeOtherItemDAL someOtherItemDAL = new SomeOtherItemDAL(); 

     // *** this must be done in one transaction *** 
     itemDAL.Add(item); 
     someOtherItemDAL.Add(someOtherItem); 
    } 
} 

現在的問題是,如果我想使用Transacion,我不能使用:

using (NpgsqlConnection conn = new NpgsqlConnection(connString)) 

in DAL。要使用NpgsqlTransacion對象,我必須以某種方式保持連接在兩個DAL類中都處於打開狀態並可見。
我試過使用TransacionScope這個對象,但從某些原因來說,它不適用於PostgreSQL和我正在使用的驅動程序(INSERTS只是在執行後完成,並且在TransacionScope發生異常時沒有事務回滾) 。

我所接觸到是讓更多的Singleton類來保持連接和管理事務:

public class DB 
{ 
    private static DB instance; 
    private const string connString = @"Server=localhost;Port=5432;Database=db_test;User Id=usr_test;Password=pass"; 
    private NpgsqlConnection conn; 

    private DB() 
    { 
     conn = new NpgsqlConnection(connString); 
    } 

    public static DB Instance 
    { 
     get 
     { 
      if (instance == null) 
      { 
       instance = new DB(); 
      } 
      return instance; 
     } 
    } 

    #region --- connection --- 
    public NpgsqlConnection GetOpenConnection() 
    { 
     OpenConnection(); 
     return conn; 
    } 

    private void OpenConnection() 
    { 
     if (conn.State == ConnectionState.Closed || conn.State == ConnectionState.Broken) 
      conn.Open(); 
    } 

    public void CloseConnection() 
    { 
     if (conn != null && !inTransaction) 
     { 
      conn.Close(); 
     } 
    } 
    #endregion 

    #region --- transaction --- 
    private NpgsqlTransaction trans; 
    private bool inTransaction; 
    public bool InTransaction { get { return inTransaction; } } 

    public void TransactionStart() 
    { 
     OpenConnection(); 
     trans = conn.BeginTransaction(); 
     inTransaction = true; 
    } 

    public void TransactionCommit() 
    { 
     if (inTransaction) 
     { 
      try 
      { 
       trans.Commit(); 
       trans.Dispose(); 
      } 
      finally 
      { 
       inTransaction = false; 
       CloseConnection(); 
      } 
     } 
    } 

    public void TransactionRollback() 
    { 
     if (inTransaction) 
     { 
      try 
      { 
       trans.Rollback(); 
       trans.Dispose(); 
      } 
      finally 
      { 
       inTransaction = false; 
       CloseConnection(); 
      } 
     } 
    } 
    #endregion 
} 

和重建兩種DAL添加方法來訪問連接這樣的:

//DAL - Data Access layer 
public class ItemDAL 
{ 
    public ItemDAL() 
    { 
    } 

    public void Add(Item item) 
    { 
     using (NpgsqlCommand cmd = new NpgsqlCommand()) 
     { 
      cmd.Connection = DB.Instance.GetOpenConnection(); 
      cmd.CommandText = @"INSERT INTO tbl_items (name) 
           VALUES (@name)"; 
      cmd.Parameters.AddWithValue("@name", item.Name); 
      cmd.ExecuteNonQuery(); 
     } 
     if (!DB.Instance.InTransaction) 
      DB.Instance.CloseConnection(); 
    } 
} 

public class SomeOtherItemDAL 
{ 
    public SomeOtherItemDAL() 
    { 
    } 

    public void Add(SomeOtherItem someOtherItem) 
    { 
     using (NpgsqlCommand cmd = new NpgsqlCommand()) 
     { 
      cmd.Connection = DB.Instance.GetOpenConnection(); 
      cmd.CommandText = @"INSERT INTO tbl_some_other_items (name) 
           VALUES (@name)"; 
      cmd.Parameters.AddWithValue("@name", someOtherItem.Name); 
      cmd.ExecuteNonQuery(); 
     } 
     if (!DB.Instance.InTransaction) 
      DB.Instance.CloseConnection(); 
    } 
} 

請注意,我想按照規則「儘快關閉數據庫連接」,所以當調用Add方法時沒有事務範圍,我希望它關閉連接。

所以,最終的問題是:
你怎麼看這件事,有沒有更好的方式來處理這個問題,有什麼建議?
2.我應該在DB.CloseConnecion()中處理連接嗎?我使用(NpgsqlConnection conn = ...) { ... }模式時肯定會這樣做,但由於Singleton在應用程序中一直存在,它是否有意義?連接返回到ConnectionPool Close()後,不是嗎?或者,也許我應該在每次使用後還處置一個Singleton對象(連同連接)?
3.這不是直接連接的問題,但如果我使用DTO對象(只是屬性,沒有方法),並且還有一些具有相同屬性的BusinessObjects(BO),但也包含其他方法(驗證,計算,操作等。),它可以從DTO繼承嗎?或者,也許我可以使用完整的BusinessObject在圖層之間進行轉換,然後擺脫DTO?

編輯:TransacionScope
按照要求,我從我的嘗試與TransactionScope的添加一些代碼。只需WinForm應用程序,無需處理異常。因此,當我拋出它時有一個Exception窗口,但是在數據庫中我看到值爲test1和test2的記錄。無論是在VS中執行debbug還是執行.exe執行的應用程序

using Npgsql; 
using System.Transactions; 
//... 

private void button1_Click(object sender, EventArgs e) 
{ 
    using (System.Transactions.TransactionScope scope = new TransactionScope()) 
    { 
     AddValue("test1"); 
     AddValue("test2"); 
     throw new Exception("bam!"); 
     AddValue("test3"); 
     scope.Complete(); 
    } 
} 

private void AddValue(string value) 
{ 
    string connString = "Server=localhost;Port=5432;Database=db_test;User Id=usr_test;Password=pass"; 

    using (NpgsqlConnection conn = new NpgsqlConnection(connString)) 
    { 
     conn.Open(); 
     using (NpgsqlCommand cmd = new NpgsqlCommand()) 
     { 
      cmd.Connection = conn; 
      cmd.CommandText = @"INSERT INTO tbl_test (name) 
           VALUES (@name)"; 
      cmd.Parameters.AddWithValue("@name", value); 
      cmd.ExecuteNonQuery(); 
     } 
    } 
} 
+1

我建議閱讀關於「工作單元」模式。 –

回答

2

我從來沒有Npgsql的使用,但在閱讀的Npgsql的文檔他們似乎有一定的支撐的TransactionScope()的,如果你添加在連接字符串中「爭取=真正的」

我要找的文件Npgsql的下方的 「System.Transactions的支持」 部分: http://npgsql.projects.postgresql.org/docs/manual/UserManual.html

假設的TransactionScope()沒有工作,那麼你可以做simething這樣的...

using (var scope = new System.Transactions.TransactionScope()) 
{ 
     ItemDAL itemDAL = new ItemDAL(); 
     SomeOtherItemDAL someOtherItemDAL = new SomeOtherItemDAL(); 

     // *** this must be done in one transaction *** 
     itemDAL.Add(item); 
     someOtherItemDAL.Add(someOtherItem); 

     scope.Complete() 
} 
+0

不錯。沒有看到我必須modufy連接字符串...雖然即使這樣,55000代碼有一些例外 - 它看起來像需要更多的服務器調整工作。稍後再看看。 – mj82

2

你所做的是勇敢的,但不可擴展。我不熟悉PGSQL,但這個問題是TransactionScope API設計的確切原因。

你可以用TransactionScope API顯示你的代碼嗎?確保你沒有調用scope.Complete();如果其中一種方法發生錯誤。注意不要「吃」方法內的異常,因爲在這種情況下,流程將繼續,就像沒有任何事情發生一樣。有關的TransactionScope這裏

更多閱讀: http://msdn.microsoft.com/en-us/library/ms172152.aspx

更新1

感謝您分享代碼,使用TransactionScope類。代碼對我來說看起來完全正確。根據這個(http://npgsql.projects.postgresql.org/docs/manual/UserManual.html)文檔(ChrisNeil52引用的文檔),Enlist = true應該包含在連接字符串中以便事務工作。

您可能正在處理錯誤的API。祝你好運。

我知道這聽起來很奇特,但我想嘗試的是使用不同的NpgsqlCommand構造函數。新的NpgsqlCommand(「sql查詢」,連接),而不是創建命令併爲其分配連接。它們應該是等價的。但誰知道...

+0

請參閱編輯的帖子... – mj82

+0

請看我編輯的答案....祝你好運! – xtrem