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

时间:2022-09-18 08:54:56

很多了解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所有,转载请标明出处