mj8*_*j82 5 .net c# layer n-tier-architecture
我想在我的WinForms应用程序中实现N层架构,以便从数据访问中分离(仅逻辑上 - 在一个项目中)业务逻辑,但是我对在BLL中使用transacion有一些疑问.我在互联网上找到的所有教程都是该架构的非常简单的实现(没有事务),或者对我的需求来说过于复杂.试图找到自己的方式,我已经到了这一点,我不知道处理BLL层事务的最佳方法.
我将尝试使用一些简单的示例来说明问题(所有类都在单独的文件中):
//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);
}
}
Run Code Online (Sandbox Code Playgroud)
现在,问题是,如果我想使用Transacion,我不能使用:
using (NpgsqlConnection conn = new NpgsqlConnection(connString))
Run Code Online (Sandbox Code Playgroud)
在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
}
Run Code Online (Sandbox Code Playgroud)
并重建两个DAL Add方法来访问连接,如:
//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();
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,我想遵循规则"尽快关闭数据库连接",因此在没有事务范围的情况下调用Add方法时,我希望它关闭连接.
所以,最后的问题是:
1.您如何看待它,有没有更好的方法来处理这个问题,有什么建议吗?
2.我应该在DB.CloseConnecion()中配置连接吗?我确实在使用(NpgsqlConnection conn = ...) { ... }模式时这样做,但是只要应用程序,Singleton是活着的,它有意义吗?连接返回到ConnectionPool之后Close(),不是吗?或者也许我应该在每次使用后处理一个Singleton对象(连同连接)?
3.它不是直接连接的问题,但是如果我使用DTO对象(只是属性,没有方法)并且还有一些具有相同属性的BusinessObjects(BO),还有其他方法(验证,计算,操作等),可以它是从DTO继承的吗?或者也许我可以使用完整的BusinessObject在层之间传输它,并摆脱DTO?
编辑:TransacionScope
根据要求,我使用TransactionScope添加了一些代码.简单的WinForm应用程序,没有异常处理.因此,当我抛出它时会有一个Exception窗口,但在数据库中我看到值为test1和test2的记录.在VS中进行debbuging并从.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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
我从未使用过 NpgSql,但阅读 NpgSql 的文档后发现,如果您在连接字符串中添加“enlist=true”,它们似乎对TransactionScope()有一些支持。
我正在查看以下 NpgSql 文档的“System.Transactions 支持”部分: http://npgsql.projects.postgresql.org/docs/manual/UserManual.html
假设 TransactionScope() 确实有效,那么你可以做这样的事情......
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()
}
Run Code Online (Sandbox Code Playgroud)