IUnitOfWork会适合我的场景吗?

时间:2012-09-13 05:14:50

标签: c# design-patterns repository-pattern

过去两天我一直在阅读有关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中使用存储过程吗?

2 个答案:

答案 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类中,该类始终返回单个实例(单)。