ABP
ABP是“ASP.NET Boilerplate Project”的简称。javascript
ABP的官方网站:http://www.aspnetboilerplate.comhtml
ABP在Github上的开源项目:https://github.com/aspnetboilerplatejava
ABP其余学习博客推荐及介绍:http://www.cnblogs.com/mienreal/p/4528470.htmlgit
ABP中Unit of Work概念及使用
若是这是你首次接触ABP框架或ABP的Unit of Work,推荐先看看 ABP使用及框架解析系列-[Unit of Work part.1-概念及使用]github
舒适提示
1.ABP的Unit of Work相关代码路径为:/Abp/Domain/Uow安全
2.框架实现中,代码不会贴所有的,可是会说明代码在项目中的位置,而且为了更加直观和缩短篇幅,对代码更细致的注释,直接在代码中,不要忘记看代码注释哈,。多线程
3.博客中的代码,有一些方法是能够点击的!框架
动态代理/拦截器/AOP
上面讲到Unit of Work有两个默认实现,领域服务和仓储库的每一个方法默认就是一个工做单元,这个是如何实现的呢?在方法上添加一个UnitOfWork特性也就让该方法为一个工做单元,这又是如何实现的呢?上面的标题已然暴露了答案——动态代理异步
在ABP中,使用了Castle的DynamicProxy进行动态代理,在组件注册是进行拦截器的注入,具体代码以下:async
internal static class UnitOfWorkRegistrar { public static void Initialize(IIocManager iocManager) {//该方法会在应用程序启动的时候调用,进行事件注册 iocManager.IocContainer.Kernel.ComponentRegistered += ComponentRegistered; } private static void ComponentRegistered(string key, IHandler handler) { if (UnitOfWorkHelper.IsConventionalUowClass(handler.ComponentModel.Implementation)) {//判断类型是否实现了IRepository或IApplicationService,若是是,则为该类型注册拦截器(UnitOfWorkInterceptor) handler.ComponentModel.Interceptors.Add(new InterceptorReference(typeof(UnitOfWorkInterceptor))); } else if (handler.ComponentModel.Implementation.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Any(UnitOfWorkHelper.HasUnitOfWorkAttribute)) {//或者类型中任何一个方法上应用了UnitOfWorkAttribute,一样为类型注册拦截器(UnitOfWorkInterceptor) handler.ComponentModel.Interceptors.Add(new InterceptorReference(typeof(UnitOfWorkInterceptor))); } } }public static bool IsConventionalUowClass(Type type) { return typeof(IRepository).IsAssignableFrom(type) || typeof(IApplicationService).IsAssignableFrom(type); }public static bool HasUnitOfWorkAttribute(MemberInfo methodInfo) { return methodInfo.IsDefined(typeof(UnitOfWorkAttribute), true); }拦截器UnitOfWorkInterceptor实现了IInterceptor接口,在调用注册了拦截器的类的方法时,会被拦截下来,而去执行IInterceptor的Intercept方法,下面是Intercept方法的代码实现:
public void Intercept(IInvocation invocation) { if (_unitOfWorkManager.Current != null) {//若是当前已经在工做单元中,则直接执行被拦截类的方法 invocation.Proceed(); return; } //获取方法上的UnitOfWorkAttribute,若是没有返回NULL,invocation.MethodInvocationTarget为被拦截类的类型 var unitOfWorkAttr = UnitOfWorkAttribute.GetUnitOfWorkAttributeOrNull(invocation.MethodInvocationTarget); if (unitOfWorkAttr == null || unitOfWorkAttr.IsDisabled) {//若是当前方法上没有UnitOfWorkAttribute或者是设置为Disabled,则直接调用被拦截类的方法 invocation.Proceed(); return; } //走到这里就表示是须要将这个方法做为工做单元了,详情点击查看 PerformUow(invocation, unitOfWorkAttr.CreateOptions()); }internal static UnitOfWorkAttribute GetUnitOfWorkAttributeOrNull(MemberInfo methodInfo) { //获取方法上标记的UnitOfWorkAttribute var attrs = methodInfo.GetCustomAttributes(typeof(UnitOfWorkAttribute), false); if (attrs.Length > 0) { return (UnitOfWorkAttribute)attrs[0]; } if (UnitOfWorkHelper.IsConventionalUowClass(methodInfo.DeclaringType)) {//若是方法上没有标记UnitOfWorkAttribute,可是方法的所属类实现了IRepository或IApplicationService,则返回一个默认UnitOfWorkAttribute return new UnitOfWorkAttribute(); } return null; }private void PerformUow(IInvocation invocation, UnitOfWorkOptions options) { if (AsyncHelper.IsAsyncMethod(invocation.Method)) {//被拦截的方法为异步方法 PerformAsyncUow(invocation, options); } else {//被拦截的方法为同步方法 PerformSyncUow(invocation, options); } }private void PerformSyncUow(IInvocation invocation, UnitOfWorkOptions options) { //手动建立一个工做单元,将被拦截的方法直接放在工做单元中 using (var uow = _unitOfWorkManager.Begin(options)) { invocation.Proceed(); uow.Complete(); } }private void PerformAsyncUow(IInvocation invocation, UnitOfWorkOptions options) { //异步方法的处理相对麻烦,须要将工做单元的Complete和Dispose放到异步任务中 var uow = _unitOfWorkManager.Begin(options); invocation.Proceed(); if (invocation.Method.ReturnType == typeof(Task)) {//若是是无返回值的异步任务 invocation.ReturnValue = InternalAsyncHelper.AwaitTaskWithPostActionAndFinally( (Task)invocation.ReturnValue, async () => await uow.CompleteAsync(), exception => uow.Dispose() ); } else //Task<TResult> {//若是是有返回值的异步任务 invocation.ReturnValue = InternalAsyncHelper.CallAwaitTaskWithPostActionAndFinallyAndGetResult( invocation.Method.ReturnType.GenericTypeArguments[0], invocation.ReturnValue, async () => await uow.CompleteAsync(), (exception) => uow.Dispose() ); } }/// <summary> /// 修改异步返回结果,而且使用try...catch...finally /// </summary> /// <param name="actualReturnValue">方法原始返回结果</param> /// <param name="postAction">指望返回结果</param> /// <param name="finalAction">不管是否异常都将执行的代码</param> /// <returns>新的异步结果</returns> public static async Task AwaitTaskWithPostActionAndFinally(Task actualReturnValue, Func<Task> postAction, Action<Exception> finalAction) { Exception exception = null; //在方法被拦截方法执行前调用工做单元的Begin,修改异步结果为调用工做单元的CompleteAsync方法,并保证工做单元会被Dispose掉 try { await actualReturnValue; await postAction(); } catch (Exception ex) { exception = ex; throw; } finally { finalAction(exception); } }public static object CallAwaitTaskWithPostActionAndFinallyAndGetResult(Type taskReturnType, object actualReturnValue, Func<Task> action, Action<Exception> finalAction) { //有返回值的异步任务重写更为复杂,须要先经过反射来为泛型传值,而后才可调用泛型方法来重写异步返回值 return typeof (InternalAsyncHelper) .GetMethod("AwaitTaskWithPostActionAndFinallyAndGetResult", BindingFlags.Public | BindingFlags.Static) .MakeGenericMethod(taskReturnType) .Invoke(null, new object[] { actualReturnValue, action, finalAction }); }public static async Task<T> AwaitTaskWithPostActionAndFinallyAndGetResult<T>(Task<T> actualReturnValue, Func<Task> postAction, Action<Exception> finalAction) { Exception exception = null; //该方法与以前无返回值的异步任务调用的方法相同,只是多了个泛型 try { var result = await actualReturnValue; await postAction(); return result; } catch (Exception ex) { exception = ex; throw; } finally { finalAction(exception); } }总结来讲,就是经过拦截器在执行方法的时候,先判断是否须要进行工做单元操做。若是须要,则在执行方法前开启工做单元,在执行方法后关闭工做单元。
在上面的代码中,咱们能够看到,工做单元都是经过_unitOfWorkManager(IUnitOfWorkManager)这样一个对象进行的,下面咱们就来解析这个类究竟是如何进行单元控制的。
IUnitOfWorkManager、IUnitOfWorkCompleteHandle
public interface IUnitOfWorkManager { IActiveUnitOfWork Current { get; } IUnitOfWorkCompleteHandle Begin(); IUnitOfWorkCompleteHandle Begin(TransactionScopeOption scope); IUnitOfWorkCompleteHandle Begin(UnitOfWorkOptions options); }ABP中,默认将UnitOfWorkManager做为IUnitOfWorkManager做为实现类,其实现中,Current直接取得ICurrentUnitOfWorkProvider对象的Current属性,后续解析ICurrentUnitOfWorkProvider。而IUnitOfWorkManager的三个Begin只是重载,最后都将调用第三个Begin的重载方法。下面是它的代码实现:
public IUnitOfWorkCompleteHandle Begin(UnitOfWorkOptions options) { //为未赋值的参数设置默认值 options.FillDefaultsForNonProvidedOptions(_defaultOptions); if (options.Scope == TransactionScopeOption.Required && _currentUnitOfWorkProvider.Current != null) {//若是当前Scope的设置为Required(而非RequiredNew),而且当前已存在工做单元,那么久返回下面这样的一个对象 return new InnerUnitOfWorkCompleteHandle(); } //走到这里,表示须要一个新的工做单元,经过IoC建立IUnitOfWork实现对象,而后开始工做单元,并设置此工做单元为当前工做单元 var uow = _iocResolver.Resolve<IUnitOfWork>(); uow.Completed += (sender, args) => { _currentUnitOfWorkProvider.Current = null; }; uow.Failed += (sender, args) => { _currentUnitOfWorkProvider.Current = null; }; uow.Disposed += (sender, args) => { _iocResolver.Release(uow); }; uow.Begin(options); _currentUnitOfWorkProvider.Current = uow; return uow; }public IActiveUnitOfWork Current { get { return _currentUnitOfWorkProvider.Current; } }internal void FillDefaultsForNonProvidedOptions(IUnitOfWorkDefaultOptions defaultOptions) { if (!IsTransactional.HasValue) { IsTransactional = defaultOptions.IsTransactional; } if (!Scope.HasValue) { Scope = defaultOptions.Scope; } if (!Timeout.HasValue && defaultOptions.Timeout.HasValue) { Timeout = defaultOptions.Timeout.Value; } if (!IsolationLevel.HasValue && defaultOptions.IsolationLevel.HasValue) { IsolationLevel = defaultOptions.IsolationLevel.Value; } }Begin方法最后返回的对象继承自IUnitOfWorkCompleteHandle,让咱们看看IUnitOfWorkCompleteHandle的接口声明又是什么样的:
public interface IUnitOfWorkCompleteHandle : IDisposable { void Complete(); Task CompleteAsync(); }总共也就两个方法,并且意思相同,都是用来完成当前工做单元的,一个同步一个异步。同时实现了IDisposable接口,结合IUnitOfWorkManager使用Begin的方式即可理解其含义(使用using)。
在以前的Begin实现中,咱们看到,其返回路线有两个,一个返回了InnerUnitOfWorkCompleteHandle对象,另外一个返回了IUnitOfWork实现对象。IUnitOfWork稍后详细解析,让咱们先来解析InnerUnitOfWorkCompleteHandle :
internal class InnerUnitOfWorkCompleteHandle : IUnitOfWorkCompleteHandle { public const string DidNotCallCompleteMethodExceptionMessage = "Did not call Complete method of a unit of work."; private volatile bool _isCompleteCalled; private volatile bool _isDisposed; public void Complete() { _isCompleteCalled = true; } public async Task CompleteAsync() { _isCompleteCalled = true; } public void Dispose() { if (_isDisposed) { return; } _isDisposed = true; if (!_isCompleteCalled) { if (HasException()) { return; } throw new AbpException(DidNotCallCompleteMethodExceptionMessage); } } private static bool HasException() { try { return Marshal.GetExceptionCode() != 0; } catch (Exception) { return false; } } }咱们能够看到,Complete和CompleteAsync里面,都只是简单的将_isCompleteCalled设置为true,在Dispose方法中,也仅仅只是判断是否已经dispose过、是否完成、是否有异常,没有更多的动做。这样你们会不会有一个疑问“这个工做单元的完成和释放工做里没有具体的完成操做,怎么就算完成工做单元了?”,这时,让咱们结合使用到InnerUnitOfWorkCompleteHandle的地方来看:
if (options.Scope == TransactionScopeOption.Required && _currentUnitOfWorkProvider.Current != null) { return new InnerUnitOfWorkCompleteHandle(); }这个判断,表明了当前已经存在工做单元,因此这个就是用于工做单元嵌套。内部的工做单元在提交和释放时,不须要作实际的提交和释放,只须要保证没有异常抛出,而后最外层工做单元再进行实际的提交和释放。这也就说明,在Begin方法中的另外一条路线,返回IUnitOfWork实现对象才是最外层的事务对象。
IUnitOfWork
public interface IUnitOfWork : IActiveUnitOfWork, IUnitOfWorkCompleteHandle { //惟一的标识ID string Id { get; } //外层工做单元 IUnitOfWork Outer { get; set; } //开始工做单元 void Begin(UnitOfWorkOptions options); }IUnitOfWork除了继承了IUnitOfWorkCompleteHandle接口,拥有了Complete方法外,还继承了IActiveUnitOfWork接口:
public interface IActiveUnitOfWork { //三个事件 event EventHandler Completed; event EventHandler<UnitOfWorkFailedEventArgs> Failed; event EventHandler Disposed; //工做单元设置 UnitOfWorkOptions Options { get; } //数据过滤配置 IReadOnlyList<DataFilterConfiguration> Filters { get; } bool IsDisposed { get; } void SaveChanges(); Task SaveChangesAsync(); IDisposable DisableFilter(params string[] filterNames); IDisposable EnableFilter(params string[] filterNames); bool IsFilterEnabled(string filterName); IDisposable SetFilterParameter(string filterName, string parameterName, object value); }这个接口中包含了不少Filter相关的属性与方法,都是些数据过滤相关的,这里不对其进行介绍,因此这个接口里,主要包含了三个事件(Completed、Failed、Disposed),工做单元设置(Options),IsDisposed以及同步异步的SaveChanges。
除了IUnitOfWork接口,ABP还提供了一个实现IUnitOfWork接口的抽象基类UnitOfWorkBase,UnitOfWorkBase的主要目的,是为开发人员处理一些前置、后置工做和异常处理,因此UnitOfWorkBase实现了部分方法,并提供了一些抽象方法,在实现的部分中作好先后置工做,而后调用抽象方法,将主要实现交给子类,并对其进行异常处理,下面以Begin实现与Complete实现为例:
public void Begin(UnitOfWorkOptions options) { if (options == null) { throw new ArgumentNullException("options"); } //防止Begin被屡次调用 PreventMultipleBegin(); Options = options; //过滤配置 SetFilters(options.FilterOverrides); //抽象方法,子类实现 BeginUow(); } public void Complete() { //防止Complete被屡次调用 PreventMultipleComplete(); try { //抽象方法,子类实现 CompleteUow(); _succeed = true; OnCompleted(); } catch (Exception ex) { _exception = ex; throw; } }private void PreventMultipleBegin() { if (_isBeginCalledBefore) {//若是已经调用过Begin方法,再次调用则抛出异常 throw new AbpException("This unit of work has started before. Can not call Start method more than once."); } _isBeginCalledBefore = true; }private void PreventMultipleComplete() { if (_isCompleteCalledBefore) {//若是已经掉用过Complete方法,再次调用则抛出异常 throw new AbpException("Complete is called before!"); } _isCompleteCalledBefore = true; }UnitOfWorkBase的其余实现与上面的相似(非Unit of Work相关内容略去),便不全贴出。可是上面的代码,你们有发现一个问题吗? 就是那些PreventMultiple…方法的实现,用来防止屡次Begin、Complete等。这些方法的实现,在单线程下是没有问题的,可是里面并无加锁,因此不适用于多线程。这是做者的BUG吗? 然而,这却并非BUG,而是设计如此,为什么呢?由于在设计上,一个线程共用一个工做单元,也就不存在多线程了。关于这部份内容,后续将会介绍。
IUnitOfWorkManager目的是提供一个简洁的IUnitOfWork管理对象,而IUnitOfWork则提供了整个工做单元须要的全部控制(Begin、SaveChanges、Complete、Dispose)。而具体应该如何保证一个线程共用一个工做单元,如何获取当前的工做单元,则由ICurrentUnitOfWorkProvider进行管控,正如在解析UnitOfWorkManager时,说明了它的Current实际上就是调用ICurrentUnitOfWorkProvider实现对象的Current属性。
ICurrentUnitOfWorkProvider
public interface ICurrentUnitOfWorkProvider { IUnitOfWork Current { get; set; } }ICurrentUnitOfWorkProvider仅仅只声明了一个Current属性,那么重点让咱们来看看Current在实现类(CallContextCurrentUnitOfWorkProvider)中是如何写的吧:
[DoNotWire] public IUnitOfWork Current { get { return GetCurrentUow(Logger); } set { SetCurrentUow(value, Logger); } }上面标注的DoNotWire是为了避免让IoC进行属性注入,Current内部分别调用了GetCurrentUow和SetCurrentUow,要取值,先要设值,让我来先看看set吧:
private static void SetCurrentUow(IUnitOfWork value, ILogger logger) { if (value == null) {//若是在set的时候设置为null,便表示要退出当前工做单元 ExitFromCurrentUowScope(logger); return; } //获取当前工做单元的key var unitOfWorkKey = CallContext.LogicalGetData(ContextKey) as string; if (unitOfWorkKey != null) { IUnitOfWork outer; if (UnitOfWorkDictionary.TryGetValue(unitOfWorkKey, out outer)) { if (outer == value) { logger.Warn("Setting the same UOW to the CallContext, no need to set again!"); return; } //到这里也就表示当前存在工做单元,那么再次设置工做单元,不是替换掉当前的工做单元而是将当前工做单元做为本次设置的工做单元的外层工做单元 value.Outer = outer; } } unitOfWorkKey = value.Id; if (!UnitOfWorkDictionary.TryAdd(unitOfWorkKey, value)) {//若是向工做单元中添加工做单元失败,便抛出异常 throw new AbpException("Can not set unit of work! UnitOfWorkDictionary.TryAdd returns false!"); } //设置当前线程的工做单元key CallContext.LogicalSetData(ContextKey, unitOfWorkKey); }private static void ExitFromCurrentUowScope(ILogger logger) { //ContextKey为一个常量字符串 //CallContext能够理解为每一个线程的独有key,value集合类,每一个线程都会有本身的存储区, // 也就是在线程A中设置一个key为xx的value,在线程B中经过xx取不到,并能够存入相同键的value,可是不会相互覆盖、影响 //根据ContextKey从线程集合中取出当前工做单元key var unitOfWorkKey = CallContext.LogicalGetData(ContextKey) as string; if (unitOfWorkKey == null) {//没有取到值,表示当前无工做单元 logger.Warn("There is no current UOW to exit!"); return; } IUnitOfWork unitOfWork; //UnitOfWorkDictionary类型为ConcurrentDictionary,线程安全字典,用于存储全部工做单元(单线程上最多只能有一个工做单元,可是多线程可能会有多个) if (!UnitOfWorkDictionary.TryGetValue(unitOfWorkKey, out unitOfWork)) {//根据key没有取到value,从线程集合(CallContext)中释放该key CallContext.FreeNamedDataSlot(ContextKey); return; } //从工做单元集合中移除当前工做单元 UnitOfWorkDictionary.TryRemove(unitOfWorkKey, out unitOfWork); if (unitOfWork.Outer == null) {//若是当前工做单元没有外层工做单元,则从线程集合(CallContext)中释放该key CallContext.FreeNamedDataSlot(ContextKey); return; } var outerUnitOfWorkKey = unitOfWork.Outer.Id; //这里也就代表了key实际上就是UnitOfWork的Id if (!UnitOfWorkDictionary.TryGetValue(outerUnitOfWorkKey, out unitOfWork)) {//若是当前工做单元有外层工做单元,可是从工做单元集合中没有取到了外层工做单元,那么一样从线程集合(CallContext)中释放该key CallContext.FreeNamedDataSlot(ContextKey); return; } //能到这里,就表示当前工做单元有外层工做单元,而且从工做单元集合中获取到了外层工做单元,那么就设外层工做单元为当前工做单元 CallContext.LogicalSetData(ContextKey, outerUnitOfWorkKey); }看完set,再让咱们来看看get吧:
private static IUnitOfWork GetCurrentUow(ILogger logger) { //获取当前工做单元key var unitOfWorkKey = CallContext.LogicalGetData(ContextKey) as string; if (unitOfWorkKey == null) { return null; } IUnitOfWork unitOfWork; if (!UnitOfWorkDictionary.TryGetValue(unitOfWorkKey, out unitOfWork)) {//若是根据key获取不到当前工做单元,那么就从当前线程集合(CallContext)中释放key CallContext.FreeNamedDataSlot(ContextKey); return null; } if (unitOfWork.IsDisposed) {//若是当前工做单元已经dispose,那么就从工做单元集合中移除,并将key从当前线程集合(CallContext)中释放 logger.Warn("There is a unitOfWorkKey in CallContext but the UOW was disposed!"); UnitOfWorkDictionary.TryRemove(unitOfWorkKey, out unitOfWork); CallContext.FreeNamedDataSlot(ContextKey); return null; } return unitOfWork; }总的说来,全部的工做单元存储在线程安全的字典对象中(ConcurrentDictionary),每一个主线程共用一个工做单元的实现,经过线程集合(CallContext)实现。
UnitOfWork实现
从上面的分析能够看出,ABP/Domain/Uow路径下,主要只是提供了一套抽象接口,并无提供实际的实现,IUnitOfWork最多也只是提供了一个UnitOfWorkBase抽象类,这样的自由性很是大,我很是喜欢这种方式。
固然ABP也另起了几个项目来提供一些经常使用的ORM的Unit of Work封装:
1.Ef: Abp.EntityFramework/EntityFramework/Uow
2.NH: Abp.NHibernate/NHibernate/Uow
3.Mongo: Abp.MongoDB/MongoDb/Uow
4.Memory: Abp.MemoryDb/MemoryDb/Uow
其中Mongo和Memory都没有进行实质性的单元操做,Ef中使用TransactionScope进行单元操做,NH中使用ITransaction来进行单元操做。
ABP/Domain/Uow结构说明
UnitOfWorkRegistrar····································注册拦截器,实现两种默认的UnitOfWork,详见最上面的默认行为
UnitOfWorkInterceptor··································Unit of Work拦截器,实现以AOP的方式进行注入单元控制
IUnitOfWorkManager····································简洁的UnitOfWork管理对象
UnitOfWorkManager··································IUnitOfWorkManager默认实现
ICurrentUnitOfWorkProvider···························当前UnitOfWork管理对象
CallContextCurrentUnitOfWorkProvider············ICurrentUnitOfWorkProvider默认实现
IUnitOfWork···············································工做单元对象(Begin、SaveChanges、Complete、Dispose)
UnitOfWorkBase·······································IUnitOfWork抽象实现类,封装实际操做的先后置操做及异常处理
NullUnitOfWork········································IUnitOfWork空实现
IActiveUnitOfWork·······································IUnitOfWork操做对象,不包含Begin与Complete操做
IUnitOfWorkCompleteHandle··························工做单元完成对象,用于实现继承工做单元功能
InnerUnitOfWorkCompleteHandle··················IUnitOfWorkCompleteHandle实现之一,用于继承外部工做单元
IUnitOfWorkDefaultOptions····························UnitOfWork默认设置
UnitOfWorkDefaultOptions··························IUnitOfWorkDefaultOptions默认实现
UnitOfWorkOptions·····································UnitOfWork配置对象
UnitOfWorkAttribute····································标记工做单元的特性
UnitOfWorkFailedEventArgs··························UnitOfWork的Failed事件参数
UnitOfWorkHelper·····································工具类
AbpDataFilters·········································数据过滤相关
DataFilterConfiguration·······························数据过滤相关