过去两天我一直在阅读有关UnitOfWork模式的内容,但没有正确理解。
我为每个班级都有一个存储库。 例如。 员工,出勤,薪资,部门,地址等。
存储库意味着 - 每个类都有自己的CRUD操作(创建自己的数据库连接)。这些会创建单独的数据库连接并插入。
当我想以原子方式执行多个操作时会出现问题。例如。在单个事务中插入Employee和Address。但是我的存储库不允许这样做,因为Employee只负责管理Employee表。
我可以使用System.Transactions,但由于单个事务的大量数据库连接,这会让我发疯。
UoW会适用于我的情况,但会做出一些改变吗?
编辑:
我的示例代码
public class AccountTransactionManager
{
Properties.Settings settings = new Properties.Settings();
public void InsertAccountTransaction(AccountTransaction accountTransaction)
{
SqlParameter AccountId = new SqlParameter { ParameterName = "@AccountId", Value = accountTransaction.AccountId, Direction = ParameterDirection.Input, SqlDbType = SqlDbType.Int };
SqlParameter PAYMENT_DATE = new SqlParameter { ParameterName = "@PAYMENT_DATE", Value = accountTransaction.PAYMENT_DATE, Direction = ParameterDirection.Input, SqlDbType = SqlDbType.DateTime };
SqlParameter CURRENT_BALANCE = new SqlParameter { ParameterName = "@CURRENT_BALANCE", Value = accountTransaction.CURRENT_BALANCE, Direction = ParameterDirection.Input, SqlDbType = SqlDbType.Money };
Helper.SqlHelper.Execute(settings.SQLConnectStr
, (tran) =>
{
Helper.SqlHelper.ExecuteNonQuery(tran, CommandType.StoredProcedure, "usp_AccountTransactionInsert", AccountId, PAYMENT_DATE, CURRENT_BALANCE);
});
}
}
SqlHelper代码
public static void Execute(string connectionString, Action<SqlTransaction> CallBack)
{
if (CallBack == null)
return;
using (var con = new SqlConnection(connectionString))
{
con.Open();
using (var tran = con.BeginTransaction())
{
#region Call procedures
try
{
CallBack(tran);
tran.Commit();
}
catch (SqlException ex)
{
tran.Rollback();
throw ex;
}
finally
{
con.Close();
}
#endregion
}
}
}
要求会经常变化。我应该使用任何ORM还是创建自己的存储库。我能在ORM中使用存储过程吗?
答案 0 :(得分:2)
你有一些非常错误的假设。首先,仅仅因为您有两个存储库,并不意味着如果同时使用这两个存储库,则会有两个数据库连接。数据库连接由大多数ORM集合,例如Entity Framework和nHibernate。因此,您不知道如何处理连接,但通常可以放心,当您调用SaveChanges时,它将具有隐含的事务并作为单个原子操作发生。
其次,您忽略了ORM的一个主要优势,即导航属性和集合。如果您有一个Employee表,那么它应该有一个Address导航属性或Address of Addresses。
所以,假设你要插入一个新的员工。您创建一个新的Employee实体,然后创建一个新的Address实体,然后将该地址添加到Employee,然后将该员工添加到您的ORM。当您调用Commit或SaveChanges或导致ORM更新的任何原因时,它会更新两个实体,并且它作为单个事务发生。
同样,如果您添加多名员工,则在调用SaveChanges时,所有员工都会作为单个事务添加。
如果您认为可能需要分布式事务,则实际上只需要使用System.Transaction。或者,您真正拥有一个无法在单个工作单元中完成的多步骤流程。
你所谈论的大部分内容都是基于这样的假设,即每个知识库都是它自己的独立工作单元,这根本不是必要的假设。 UnitOfWorks可以在所有存储库之间共享。如果您使用依赖注入,则更容易实现。
编辑:
根据您的编辑,我发现您没有使用ORM,而是使用存储库来处理您的DAL。我所说的一些内容仍然适用,您不需要每个存储库都有自己的连接,并且您不需要存储库只处理单个实体,因为实体具有可以管理的相关实体。
要记住UnitOfWork的重要一点是它是一种实体缓存。您向其添加或更新脏实体,并且它不会逐个保存它们...而是等待直到您调用Save,然后它在单个事务中完成它们。
此外,Repository不是一个工作单元,它更像是UnitOfWork的包装器。您的UnitOfWork会跟踪模型中的所有实体,而不仅仅是单个实体。因此,多个存储库可以共享一个UnitOfWork。
答案 1 :(得分:1)
UnitOfWork模式用于管理Model对象的状态以及是否需要将其保存到数据库中。
最好的思考方式是,如果更改Model对象的属性,则对象知道它已被更改(通常将其标记为脏),然后此对象由持久性机制处理。您可能在IList中有10个对象 - 您更改了其中的2个 - 当保存持久层时,知道只有2个已更改并且只将这些对象保存到数据库中。
您可以详细了解其工作原理here
根据事物的声音,您遇到的真正问题是如何创建持久层(您的存储库)。您需要的是某种形式的单一连接管理对象,它保存与数据库的连接,但是以程序集中所有存储库可访问的方式执行。你需要的是一个Singleton对象,可能是使用静态工厂模式创建的。
现在说实话,这是一个相当低级别的数据访问代码并且管理事务(提交,在事务等下)和连接状态(打开,关闭)可能是一个麻烦的代码。现在有很多框架,比如nHibernate,Entity Framework和Active Record,它们可以为你完成所有这些工作(具有讽刺意味的是使用你描述的许多模式)。
编辑:刚看到你的代码。如果你真的,真的想自己做,那么从SQLHelper类中删除static关键字,将连接代码移出到名为open和close的单独方法中,并将SQLHelper类包装在一个新的静态Factory类中,该类始终返回单个实例(单)。