• Unit Of Work(工作单元)模式探索


    什么是Unit Of Work模式

    Unit Of Work(工作单元)模式用来维护一个由已经被业务事物修改(增加、删除或更新)的业务对象组成的列表。Unit  Of Work模式负责协调这些修改的持久化工作以及所有标记的并发问题。在数据访问层中采用Unit Of Work模式带来的好处是能够确保数据完整性。如果在持久化一系列业务对象(他们属于同一个事物)的过程中出现问题,那么应该将所有的修改回滚,以确保数据始终处于有效状态。

    为了演示Unit Of Work模式,使用一个简单的银行领域对两个账号之间的转账建模。下图给出了服务层(AccountService)与使用了Unit Of Work模式(以确保转账作为原子事物的Unit Of Work提交)的资源层(AccountRepository)之间的交互。

    记住这张图,因为下面的代码逻辑都是依照这张图的定义来实现的。

    建立Infrastructure

    • 下面开始编写解决方案的代码,首先创建Unit Of Work模式的所有配套的基础设施代码
     public interface IAggregateRoot
        {
        }

    IAggregateRoot接口实际上属于标记接口,这个接口充当了类和方法的元数据,我们构建的资源库只持久化实现了IAggregateRoot接口的业务对象,所以Unit Of Work的实现将使用IAggregateRoot接口来引用原子事物中涉及的任何业务实体

    • 添加另外一个接口IUnitOfWorkRepository,这是一个用来持久化操作的接口:
     public interface IUnitOfWorkRepository
        {
            void PersistCreationOf(IAggregateRoot entity);
            void PersistUpdateOf(IAggregateRoot entity);
            void PersistDeletionOf(IAggregateRoot entity);
        }
    • 之后,向Infrastructure项目中添加IUnitOfWork接口:
    复制代码
     public interface IUnitOfWork
        {
            void RegisterAmended(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository);
            void RegisterNew(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository);
            void RegisterRemoved(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository);
            void Commit();
        }
    复制代码

     值得注意的是IUnitOfWork接口在注册修改/增加/删除时需要IUnitOfWorkRepository,这样在提交时,Unit Of Work可以将真正持久化的工作委托给适当的具体实现。

    • 最后向Infrastructure项目中添加UnitOfWork,实现IUnitOfWork:
    复制代码
     public class UnitOfWork : IUnitOfWork 
        {
            private Dictionary<IAggregateRoot, IUnitOfWorkRepository> addedEntities;
            private Dictionary<IAggregateRoot, IUnitOfWorkRepository> changedEntities;
            private Dictionary<IAggregateRoot, IUnitOfWorkRepository> deletedEntities;
    
            public UnitOfWork()
            {
                addedEntities = new Dictionary<IAggregateRoot, IUnitOfWorkRepository>();
                changedEntities = new Dictionary<IAggregateRoot, IUnitOfWorkRepository>();
                deletedEntities = new Dictionary<IAggregateRoot, IUnitOfWorkRepository>();
            }
    
            public void RegisterAmended(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository)
            {
                if (!changedEntities.ContainsKey(entity))
                {
                    changedEntities.Add(entity, unitofWorkRepository);
                }
            }
    
            public void RegisterNew(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository)
            {
                if (!addedEntities.ContainsKey(entity))
                {
                    addedEntities.Add(entity, unitofWorkRepository);
                };
            }
    
            public void RegisterRemoved(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository)
            {
                if (!deletedEntities.ContainsKey(entity))
                {
                    deletedEntities.Add(entity, unitofWorkRepository);
                }
            }
            
            public void Commit()
            {
                using (TransactionScope scope = new TransactionScope())
                {
                    foreach (IAggregateRoot entity in this.addedEntities.Keys)
                    {
                        this.addedEntities[entity].PersistCreationOf(entity);
                    }
    
                    foreach (IAggregateRoot entity in this.changedEntities.Keys)
                    {
                        this.changedEntities[entity].PersistUpdateOf(entity);
                    }
    
                    foreach (IAggregateRoot entity in this.deletedEntities.Keys)
                    {
                        this.deletedEntities[entity].PersistDeletionOf(entity);
                    }
    
                    scope.Complete(); 
                }
            }
    
        }
    复制代码

    UnitOfWork类使用3个字典变量来跟踪对业务实体的代执行修改。第一个字典对应于被添加到数据存储的实体,第2个字典跟踪带更新的实体,而第三个字典处理实体删除,与字典中的实体键匹配的IUnitOfWorkRepository将被保存下来,并用于Commit方法之中,来调用Repository对象,该对象包含真正持久化实体的代码。Commit方法遍历每一个字典,并调用相应的IUnitOfWorkRepository方法(传递实体引用)。Commit方法中的工作均被TransactionScope代码包装起来,如果在IUnitOfWorkRepository中执行任务时出现异常,则所有工作回滚,数据存储将保持原来的状态。

    建立Model

    • 向Model中添加一个新类Account,表示银行账户,为了方便演示简单处理了:
    public class Account : IAggregateRoot
        {
            public decimal balance { get; set; }
        }
    •   为了将Accout持久化,添加IAccountRepository接口:
    public interface IAccountRepository
        {
            void Save(Account account);
            void Add(Account account);
            void Remove(Account account);                
        }
    • 添加AccountService服务类来协调两个账户之间的转账工作。
    复制代码
    public class AccountService
        {
            private IAccountRepository _accountRepository;
            private IUnitOfWork _unitOfWork;
            /// <summary>
            /// AccountService通过其构造器实现依赖注入
            /// </summary>
            /// <param name="accountRepository"></param>
            /// <param name="unitOfWork"></param>
            public AccountService(IAccountRepository accountRepository,
                                  IUnitOfWork unitOfWork)
            {
                _accountRepository = accountRepository;
                _unitOfWork = unitOfWork;            
            }
            /// <summary>
            /// 实现两个账户之间转账工作
            /// </summary>
            /// <param name="from"></param>
            /// <param name="to"></param>
            /// <param name="amount"></param>
            public void Transfer(Account from, Account to, decimal amount)
            {
                if (from.balance >= amount)
                {
                    from.balance -= amount;
                    to.balance += amount;
    
                    _accountRepository.Save(from);
                    _accountRepository.Save(to);
                    _unitOfWork.Commit();
                }
            }
        }
    复制代码

    接着,它调用账户Repository来保存两个账户,最后,它调用Unit Of Work实例的Commit方法来确保该交易作为原子的Unit Of Work完成。所以接下来的重点就是怎样Repository与Unit Of Work交互。

    建立Repository来持久化业务实体

    复制代码
     public class AccountRepository : IAccountRepository, IUnitOfWorkRepository 
        {
            private IUnitOfWork _unitOfWork;
    
            public AccountRepository(IUnitOfWork unitOfWork)
            {
                _unitOfWork = unitOfWork;
            }
    
            public void Save(Account account)
            {            
                _unitOfWork.RegisterAmended(account, this);            
            }
    
            public void Add(Account account)
            {
                _unitOfWork.RegisterNew(account, this);
            }
    
            public void Remove(Account account)
            {
                _unitOfWork.RegisterRemoved(account, this);
            }  
    
            public void PersistUpdateOf(IAggregateRoot entity)
            {
                // ADO.net or EF、NH来持久化
            }
    
            public void PersistCreationOf(IAggregateRoot entity)
            {
                // ADO.net or EF、NH来持久化
            }
    
            public void PersistDeletionOf(IAggregateRoot entity)
            {
                // ADO.net or EF、NH来持久化
            }              
        }
    复制代码

    OK,这样Unit Of Work工作模式就搭建好了,AccountRepository实现了IAccountRepositoryIUnitOfWorkRepository接口,IAccountRepository方法的实现简单地将工作委托给Unit Of Work(传入待持久化的实体以及Repository的引用),最后,调用Unit Of Work类的Commit方法,其实是Unit Of Work引用Repository的IUnitOfWorkRepository的接口契约来真正完成持久化任务,至于持久化操作你可以用Ado.net或者EF、NH等。

    回过头来再看这幅图,原来Unit Of Work也就是如此罢了:

    本博客为木宛城主原创,基于Creative Commons Attribution 2.5 China Mainland License发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名木宛城主(包含链接)。如您有任何疑问或者授权方面的协商,请给我留言。
  • 相关阅读:
    4.VS2010C++建立DLL工程
    C++-教程2-VS2010C++相关文件说明
    C++-教程1-VS2010环境设置
    Android实例-实现扫描二维码并生成二维码(XE8+小米5)
    C++-教程3-VS2010C++各种后缀说明
    Android问题-No resource found that matches the given name (at 'theme' with value '@style/CaptureTheme').
    Android问题-新电脑新系统WIN764位上安装简版本的XE8提示“Unit not found: 'System'”
    启动程序的同时传参给接收程序(XE8+WIN764)
    Android实例-程序切换到后台及从后台切换到前台
    Unity向量投影使用
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2747553.html
Copyright © 2020-2023  润新知