• ASP.NET MVC4+EF系列之七仓储中的事务实现 实现IUnitOfWork接口


    很多了解EF的都知道。EF本身的SaveChange()方法是自带事务功能。就是在SaveChange()方法之前的对DbConext的操作都会被当成一个事务去处理。当是在一个框架系统中,SaveChange也许是满足不了需求的。所以就让IUnitOfWork的存在提供了必要条件。IUnitOfWork只是一个接口,为我们提供一个规范。至于具体实现各个ORM是不一样的。当时只要按照这个标准去做我们都是支持的。:)。

    /*****************************************************
     * 作者:egojit
     * 日期:2012-7-13
     * 描述:事务接口
     * ***************************************************/
    namespace EgojitFramework.Domain
    {
        /// <summary>
        /// 单元工作,为了管理事物
        /// </summary>
        public interface IUnitOfWork
        {
            /// <summary>
            ///  
            /// 返回一个Bool型用于标识单元工作是否被提交
            /// </summary>
            bool Committed { get; }
            /// <summary>
            /// 提交单元工作
            /// </summary>
            void Commit();
            /// <summary>
            /// 回滚单元工作
            /// </summary>
            void Rollback();
        }
    }

    大家可以看到这个借口很简单,一个属性用于判定事务是否已经被提交。另外两个方法就顾名思义,一个提交事务,一个回滚事务。

    接下来不用说,我们关注他的继承类和事务是如何去实现的。大家打开我提供的源码可以看到它被RepositoryContextManager类继承。

     #region IUnitOfWork 成员
            /// <summary>
            /// 判断事务是否被提交
            /// </summary>
            public bool Committed
            {
                get { return context.Committed; }
            }
            /// <summary>
            ///提交事务
            /// </summary>
            public void Commit()
            {
                context.Commit();
            }
            /// <summary>
            /// 回滚事务
            /// </summary>
            public void Rollback()
            {
                context.Rollback();
            }
    
            #endregion

    从这段代码大家看不到任何东西,但是别急。这个我只是和大家分析一个实现过程。而上面的context其实是IRepositoryContext类型。大家打开这个接口很容易看到它也继承了IUnitOfWork这会大家忽然明白所有的实现在IRepositoryContext的实现类中。RepositoryContext类继承了IRepositoryContext接口,然后我们从代码中发现关于IUnitOfWork接口的方法欧式抽象的abstract的,那么它肯定被实现。不难理解其实这个RepositoryContext仓储环境还只是一般的,而非针对特殊的ORM去实现的。在此我们很容易想到我们这里的ORM工具是EF那么它的实现肯定在EntityFrameworkRepositoryContext类中,对了,我们猜测的没错。EntityFrameworkRepositoryContext:

    public class EntityFrameworkRepositoryContext : RepositoryContext, IEntityFrameworkRepositoryContext

    我们看到他继承了RepositoryContext这个类,那我们在回到这个类:

    using System;
    using System.Collections.Generic;
    using EgojitFramework.Infrastructure;
    /*****************************************************
     * 作者:egojit
     * 日期:2012-7-13
     * 描述:仓储上下文基础类
     * ***************************************************/
    namespace EgojitFramework.Domain.Repositories
    {
        /// <summary>
        ///仓储上下文基础类
        /// </summary>
        public abstract class RepositoryContext : DisposableObject, IRepositoryContext
        {
            #region 私有字段
            private readonly Guid id = Guid.NewGuid();
            [ThreadStatic]
            private readonly Dictionary<Guid, object> newCollection = new Dictionary<Guid, object>();
            [ThreadStatic]
            private readonly Dictionary<Guid, object> modifiedCollection = new Dictionary<Guid, object>();
            [ThreadStatic]
            private readonly Dictionary<Guid, object> deletedCollection = new Dictionary<Guid, object>();
            [ThreadStatic]
            private bool committed = true;
            #endregion

    发现还有这样一段代码,他们就是用来放那些,删除,修改,添加的对象,用GUID作为Key,这个就是本地内存,首先将对象的改变都放在这三个集合中。可以看看

    RepositoryContext类中对IRepositoryContext接口的实现这里我就贴其中的一个代码
            /// <summary>
            /// 注册一个新的对象到仓储环境
            /// </summary>
            /// <typeparam name="TAggregateRoot">仓储根类型.</typeparam>
            /// <param name="obj">被注册的对象的名字.</param>
            public virtual void RegisterNew<TAggregateRoot>(TAggregateRoot obj) where TAggregateRoot : class, IAggregateRoot
            {
                if (obj.ID.Equals(Guid.Empty))
                    throw new ArgumentException("The ID of the object is empty.", "obj");
                if (modifiedCollection.ContainsKey(obj.ID))
                    throw new InvalidOperationException("The object cannot be registered as a new object since it was marked as modified.");
                if (newCollection.ContainsKey(obj.ID))
                    throw new InvalidOperationException("The object has already been registered as a new object.");
                newCollection.Add(obj.ID, obj);
                committed = false;
            }

    这样将他们放入字典中。并且 committed = false;再回到EntityFrameworkRepositoryContext类中我们看看事务的实现代码

    /*****************************************************
     * 作者:egojit
     * 日期:2012-8-14
     * 描述:角色服务
     * ***************************************************/
    using System;
    using System.Data.Entity;
    using System.Data.Entity.Validation;
    using EgojitFramework.Domain.Model;
    
    namespace EgojitFramework.Domain.Repositories
    {
        public class EntityFrameworkRepositoryContext : RepositoryContext, IEntityFrameworkRepositoryContext
        {
            private readonly EgojitFrameworkContext ctx = new EgojitFrameworkContext();
            private readonly object sync = new object();
    
            public override void RegisterDeleted<TAggregateRoot>(TAggregateRoot obj)
            {
                ctx.Entry<TAggregateRoot>(obj).State = System.Data.EntityState.Deleted;
                Committed = false;
            }
    
            public override void RegisterModified<TAggregateRoot>(TAggregateRoot obj)
            {
                ctx.Entry<TAggregateRoot>(obj).State = System.Data.EntityState.Modified;
                Committed = false;
            }
    
            public override void RegisterNew<TAggregateRoot>(TAggregateRoot obj)
            {
                ctx.Entry<TAggregateRoot>(obj).State = System.Data.EntityState.Added;
                Committed = false;
            }
    
            public override void Commit()
            {
                if (!Committed)
                {
                    lock (sync)
                    {
                        try
                        {
                            ctx.SaveChanges();
                            Committed = true;
                        }
                        catch (DbEntityValidationException e)
                        {
                            foreach (var eve in e.EntityValidationErrors)
                            {
                                Console.WriteLine("Entity of type \"{0}\" in state \"{1}\" has the following validation errors:",
                                    eve.Entry.Entity.GetType().Name, eve.Entry.State);
                                foreach (var ve in eve.ValidationErrors)
                                {
                                    Console.WriteLine("- Property: \"{0}\", Error: \"{1}\"",
                                        ve.PropertyName, ve.ErrorMessage);
                                }
                            }
                            throw;
                        }
                    }
                }
            }
    
            public override void Rollback()
            {
                Committed = false;
            }

    至此我们了解了整个事务的实现过程。这样实现的事务是不是更加灵活。我们不需要频繁的去链接数据库,所有的操作都在本地内存。直到我们提交。

    版权:本文属博客园和egojit所有,转载请标明出处

  • 相关阅读:
    GAMES101作业1:旋转与投影
    ant design vue关于input组件设置只读
    使用事件代理解决v-html点击事件无效
    js替换字符串中的空格,换行符 或 替换成<br>
    vue中ref的使用(this.$refs获取为undefined)
    轮询锁在使用时遇到的问题与解决方案!
    死锁终结者:顺序锁和轮询锁!
    死锁的 4 种排查工具 !
    图解:为什么非公平锁的性能更高?
    抽奖动画
  • 原文地址:https://www.cnblogs.com/egojit/p/3077623.html
Copyright © 2020-2023  润新知