我想在我的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();
}
}
}
我建議閱讀關於「工作單元」模式。 –