[Abp 源码分析]六、工作单元的实现 (2)

下面我们就来看一下刚刚注入的拦截器:

internal class UnitOfWorkInterceptor : IInterceptor { // ... // 其他代码 public void Intercept(IInvocation invocation) { MethodInfo method; try { method = invocation.MethodInvocationTarget; } catch { method = invocation.GetConcreteMethod(); } // 判断当前进入的方法是否带有 UnitOfWork 特性 var unitOfWorkAttr = _unitOfWorkOptions.GetUnitOfWorkAttributeOrNull(method); if (unitOfWorkAttr == null || unitOfWorkAttr.IsDisabled) { // 没有则直接执行该方法 invocation.Proceed(); return; } PerformUow(invocation, unitOfWorkAttr.CreateOptions()); } // ... // 其他代码 }

拦截器内部方法很简单,如果是 UOW 方法则执行 PerformUow() 即可,在该方法内部则对方法类型进行了不同的判断,同步与异步的处理方法是不一样的。

// ... // 其他代码 private void PerformUow(IInvocation invocation, UnitOfWorkOptions options) { // 判断方法是同步还是异步方法,不同则执行不同的处理操作 if (invocation.Method.IsAsync()) { PerformAsyncUow(invocation, options); } else { PerformSyncUow(invocation, options); } } // ... // 其他代码

那么我们就先来看一下同步方法:

// ... // 其他代码 private void PerformSyncUow(IInvocation invocation, UnitOfWorkOptions options) { using (var uow = _unitOfWorkManager.Begin(options)) { // 继续执行 invocation.Proceed(); uow.Complete(); } } // ... // 其他代码

同步方法针对 UOW 的操作十分简单,直接使用 UnitOfWorkManager.Begin() 方法开启一个事务,然后在内部执行原有方法的代码,执行完成之后调用 Complete() 完成此次调用。

假如我拥有两个应用服务类,他们都拥有 UnitOfWork 特性,然后我再一个 A 方法调用他们两个 B 类的 Run() 方法,而 B类的内部也调用了C 的 Run() 方法,大体如下:

public class A { private readonly B B; public A(B b) { B = b; } public void TestMethod() { B.Run(); } } internal class B { private readonly C C; public B(C c) { C = c; } [UnitOfWork] public void Run() { // 数据库操作 C.Run(); Console.WriteLine("B 的 Run 方法被调用."); } } internal class C { [UnitOfWork] public void Run() { Console.WriteLine("C 的 Run 方法被调用."); } }

然后在拦截器内部的执行过程就类似于下面这种:

internal class UnitOfWorkInterceptor { public void TestMethod() { using (var uow = _unitOfWorkManager.Begin(options)) { using(var uow2 = _unitOfWorkManager.Begin(options)) { // C 方法的代码 Console.WriteLine("C 的 Run 方法被调用."); uow2.Complete(); } // B 方法的代码 Console.WriteLine("B 的 Run 方法被调用."); uow.Complete(); } } }

两个工作单元之间的调用会被嵌套在一个 using 语句块之中,一旦任何代码抛出了异常,都会导致最外层的 uow.Complete() 不会被执行,而 Complete() 方法没有执行,则会导致 uow 对象被释放的时候,uow.Dispose() 内部检测到 Complete() 没有被调用,Abp 框架也会自己抛出异常。

所以 Abp 巧妙结合 Castle Dynamic 实现了 UOW 模式。

下面我们继续看一下他是如何处理异步 UOW 方法的。

private void PerformAsyncUow(IInvocation invocation, UnitOfWorkOptions options) { var uow = _unitOfWorkManager.Begin(options); try { invocation.Proceed(); } catch { uow.Dispose(); throw; } // 如果是无返回值的异步方法 if (invocation.Method.ReturnType == typeof(Task)) { invocation.ReturnValue = InternalAsyncHelper.AwaitTaskWithPostActionAndFinally( (Task) invocation.ReturnValue, async () => await uow.CompleteAsync(), exception => uow.Dispose() ); } // 有返回值的异步方法处理 else { invocation.ReturnValue = InternalAsyncHelper.CallAwaitTaskWithPostActionAndFinallyAndGetResult( invocation.Method.ReturnType.GenericTypeArguments[0], invocation.ReturnValue, async () => await uow.CompleteAsync(), exception => uow.Dispose() ); } }

相比而言,针对拦截到的异步方法处理起来更加复杂一点,但是总体思路仍然是一样的,将这些工作单元的方法一层层地嵌套起来,依次执行就是核心。而在上面代码里面,一样的首先使用 UnitOfManager.Begin() 获得了一个新的工作单元之后,继续执行原有的操作,下面则主要是通过内部的 InternalAsyncHelper 封装的两个辅助方法来确保等待原有任务执行完成之后,再执行 CompleteAsync() 方法。

我们可以来看一下这个内部类的实现:

// 异步无返回值处理 public static async Task AwaitTaskWithPostActionAndFinally(Task actualReturnValue, Func<Task> postAction, Action<Exception> finalAction) { Exception exception = null; try { // 等待原有任务执行完成 await actualReturnValue; // 执行 CompleteAsync() 表示本工作单元已经顺利执行 await postAction(); } // 捕获异常 catch (Exception ex) { exception = ex; throw; } finally { // 不论是否抛出异常,都调用之前传入的 uow.Dispose() 方法 finalAction(exception); } } // 原理基本同上,只是多了一个返回值 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); } } // 异步有返回值处理 public static object CallAwaitTaskWithPostActionAndFinallyAndGetResult(Type taskReturnType, object actualReturnValue, Func<Task> action, Action<Exception> finalAction) { // 这里通过反射获取到 AwaitTaskWithPostActionAndFinallyAndGetResult 方法,并调用。 return typeof (InternalAsyncHelper) .GetMethod("AwaitTaskWithPostActionAndFinallyAndGetResult", BindingFlags.Public | BindingFlags.Static) .MakeGenericMethod(taskReturnType) .Invoke(null, new object[] { actualReturnValue, action, finalAction }); }

并不复杂,以上即是拦截器所做的操作。

2.2 工作单元管理器

通过上文我们可以看到一个工作单元是通过 IUnitOfWorkManager.Begin() 拿到的,那 IUnitOfWorkManager 又是个什么东西呢?

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpyzds.html