第二节 Repository和UnitOfWork模式

本文探讨了仓储(Repository)模式和工作单元(UnitOfWork)模式在软件开发中的应用,特别是如何通过这两种模式实现数据层与业务层的解耦,提高代码的复用性和程序性能。文章还对比了不同模式下MiniProfiler的读写性能监视结果,以及显示调用Dispose方法对内存消耗的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、为什么要使用Repository模式?

Repository(仓储)模式最早是领域驱动设计 (DDD)思想提出的,实现其设计思想具体的技术手段。Repository(仓储)模式的主要解决的问题有:

  1. Repository(仓储)模式进一步对数据层中的查询、增、删、改等数据库业务操作,以及实体之间的映射进行封装,从而达到数据层与业务层(Service)的进一步解耦的目的。
  2. Repository(仓储) 模式是通过泛型(动态或模板)Repository<T>类定义实现的,通过调用Repository<T>类可以有效的减少重复代码,增强复用性,使开发者可以更有效的作更有意义的工作。
  3. IRepository接口可以更方便灵活的使用依赖注入思想及其技术,进一步分离和解耦 DbContext的继承类与程序系统中其它层之间的联系。
  4. 虽然Entity Framework中的DbSet<T>也可以达到上述Repository(仓储) 模式中的的效果,并且代码量可能还比Repository(仓储) 模式少,调用逻辑相对于进一步封装的Repository(仓储) 模式更加清晰,但是它致命的弱点在于DbContext的继承类与程序系统中其它层之间的产生强耦合的联系,这与当前主流的依赖注入,接口实现分离等程序架构设计思想严重不符。
  5. Repository(仓储) 模式可以通过记录大量的数据流增、删、改的操作状态并存储在内存中,之后在统一的持久到相应的数据库中,这样可以减少对数据库操作的频率,提高整个程序系统的性能。

二、UnitOfWork模式

对数据流操作职能的进一步细化,演化出了UnitOfWork(工作单元)模式,Repository(仓储) 模式。

       Repository(仓储)模式记录了数据流增、删、改的操作状态并存储在内存中,而UnitOfWork(工作单元)模式则负责将这些标记了操作状态的数据流持久到相应的数据库中,如果没有成功则执行回滚操作。

       Repository(仓储)模式与UnitOfWork(工作单元)模式有没本质的区别,只是通过显示的UnitOfWork(工作单元)模式,进一步细化了数据流的操作流程。实现UnitOfWork(工作单元)模式功能的方法可以完全集成进Repository(仓储)模式中,当然反之亦然。

       同时大量录了数据流增、删、改的操作状态统一的持久到相应的数据库的操作在Web程序的实际业务中, 除了删除操作,其它的操作除了管理人员外使用频率不大。其实统一持久化操作更多的应用是在From程序的实际业务中。

       在Web程序的实际开发中Repository(仓储)模式与DbContext的继承类相组合的项目比较多,但是本人更喜爱使用进一步封装的UnitOfWork(工作单元)模式,它把DbContext的继承类也封装在其中,这样数据层与其它层的耦合度进一步降低,数据层的复用性和可移植性进一步提高。

三、MiniProfiler读写性能监视与是否显示调用Dispose方法的内存消耗对比

       不管是MiniProfiler读写性能监视,还是是否显示调用Dispose方法的内存消耗对比,都是对整个程序系统性能的一个定量的性能的评估。

       执行程序“2019-12-18_NoIOCFramework(006控制台常规调用模式,MiniProfiler10组1000次读写性能监视)”与“NoIOCFramework(007控制台动态实例Repository调用模式,MiniProfiler10组1000次读写性能监视)”和“NoIOCFramework(008控制台动态实例UnitOfWork调用模式,MiniProfiler10组1000次读写性能监视)”,通过MiniProfiler对读写性能的监视,很直观的发现常规调用模式,随着调用次数的增加,其完成操作的时间是呈直线增加的。Repository调用模式和UnitOfWork调用模式完成操作的时间与调用次数没有作保的直接关系,并且完成的时间远远小于常规调用。“NoIOCFramework(010WEB动态实例UnitOfWork调用模式,MiniProfiler读写性能监视)”是用于在WEB环境中是怎样配置MiniProfiler(4.1.0)的,在WEB环境中使用MiniProfiler(4.1.0)在执行时会遇到一些异常,怎样解决这些异常参考源码中的“程序档案->日志”。

       执行程序“NoIOCFramework(008控制台动态实例UnitOfWork调用模式,MiniProfiler10组1000次读写性能监视)”与“NoIOCFramework(009控制台动态实例UnitOfWork-Dispose调用模式,MiniProfiler10组1000次读写性能监视)”,在1000次读写量级上通过诊断工具中的内存进程可以看得出是否显示调用Dispose方法对内存消耗,有着直接的有关系。如果显示调用Dispose方法在内存消耗达到高峰后,会随着调用次数进行有规律的波动,在1000次读写量级上调用Dispose方法,释放内存而产生的波峰各波谷不太显。当读写量级达到10000时,内存进程可以以更直观的显示出因为Dispose方法的调用释放内存而产生的波谷。

    /// <summary>

        /// 【释放】

        /// <param name="isDisposing">

        ///     指示是否是否正在执行销毁操作,默认值--ture;不执行--flase;正在执行--ture。

        /// </param>

        /// <remarks>

        /// 摘要:

        ///      确保EF上下文实例使用完后被显式地释放。

        /// </remarks>

        /// </summary>

        protected virtual void Dispose(bool isDisposing)

        {

            if (!_isDisposed)

            {

                if (isDisposing)

                {

                    if (!IsCommitted)

                    {

                        Commit();

                    }

                    _eFContextUnitOfWork.Dispose();

                    _eFContextUnitOfWork = null;

                }

            }

            _isDisposed = true;

        }

 

        /// <summary>

        /// 【释放】

        /// <remarks>

        /// 摘要:

        ///     如果执行了一个实例的的该方法,说明实例已经可以释放所分配的内存了,

        /// 但如果没有执行“实例 = null”语句(例:  _eFContextUnitOfWork = null;)或没有已经执行过 GC.SuppressFinalize(this);语句,

        /// 如果该实例中存在数据,则实例的所有数据依然存在于内存中。如果实例的值为null或已经执行过 GC.SuppressFinalize(this);语句,

        /// 则实例在内存中已经不存在数据,说明实例已经成功的释放所分配的内存。

        ///     释放单元操作中的DBContext实例对象或继承于DBContext的实例对象(这时指EFContext),

        /// 同时关闭数据库连接,确保调用所指定的动态模型类对象及时销毁和内存的释放,只有该方法是IDisposable成员接口的实现。

        /// 托管资源:

        ///     由CLR管理分配和释放的资源,即由CLR里new出来的对象,针对托管资源,DotNet的垃圾回收器会自动地回收托管资源。

        /// 非托管资源:

        ///     不受CLR管理的对象,windows内核对象,如文件、数据库连接、套接字、COM对象等,则需要用自定义的Dispose方法显式释放非托管资源。

        /// </remarks>

        /// </summary>

        public void Dispose()

        {

            Dispose(true);

            //如果一个实例已经调用了其Dispose方法,那么就释放该实例分配的内存。

            GC.SuppressFinalize(this);

        }

       如果程序显式的调用方法Dispose(), _eFContextUnitOfWork.Dispose()和_eFContextUnitOfWork = null语句只是以显式的式来确认释放当前实例分配的内存,这样在内存进程中内存的分配和释放可以明确的观察到其规律性。即使把两行语句其中的一行或者两行都注释掉,程序依然可能通过执行GC.SuppressFinalize(this)由系统来默认的释放当前实例分配的内存,但在内存进程中内存的分配和释放已经不可能明确的观察到其规律性。

    在对“NoIOCFramework(008控制台动态实例UnitOfWork调用模式,MiniProfiler10组1000次读写性能监视)”执行10000量级的读写操作时,内存进程中内存的分配是随着读写次数的增加而增加的,没有观察到由内存释放而产生的波谷。10000量级的读写操作时显式调用Dispose()方法内存的有在108.8149.9(MB),不调用Dispose()方法为271.2(MB)从此可以看出显式调用Dispose()方法可以显著的减少内存消耗,增加程序性能。

2019-12-18_NoIOCFramework(004控制台动态实例Repository调用模式,完成Insert、Update、Delete数据操作验证) -- https://download.youkuaiyun.com/download/zhoujian_911/12038118。

NoIOCFramework(005控制台动态实例UnitOfWork调用模式,完成Insert、Update、Delete数据操作验证) --https://download.youkuaiyun.com/download/zhoujian_911/12038126。

2019-12-18_NoIOCFramework(006控制台常规调用模式,MiniProfiler10组1000次读写性能监视) --https://download.youkuaiyun.com/download/zhoujian_911/12038131。

NoIOCFramework(007控制台动态实例Repository调用模式,MiniProfiler10组1000次读写性能监视) -- https://download.youkuaiyun.com/download/zhoujian_911/12038138。

NoIOCFramework(008控制台动态实例UnitOfWork调用模式,MiniProfiler10组1000次读写性能监视)  --https://download.youkuaiyun.com/download/zhoujian_911/12038141。

 NoIOCFramework(009控制台动态实例UnitOfWork-Dispose调用模式,MiniProfiler10组1000次读写性能监视) --https://download.youkuaiyun.com/download/zhoujian_911/12038148。

 NoIOCFramework(010WEB动态实例UnitOfWork调用模式,MiniProfiler读写性能监视)  -- https://download.youkuaiyun.com/download/zhoujian_911/12038264。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值