简介

Unit of work:维护受业务事务影响的对象列表,并协调变化的写入和并发问题的解决。即管理对象的CRUD操作,以及相应的事务与并发问题等。Unit of Work是用来解决领域模型存储和变更工作,而这些数据层业务并不属于领域模型本身具有的

我们知道在ABP中应用服务层,仓储。默认是启用工作单元模式的

若我们关闭了全局的工作单元,则必须以特性的方式在 class,method interface上加上[Unit of work]

然后ABP会通过Castle 的动态代理(Dynamic Proxy)拦截,Unit of work Attribute,进行动态注入,实现了 UnitOfworkManager对方法的管理(通过事务)

其流程如下

Abp初始化=>注册uow相关组件=>监听ioc注册事件=>是否有unitofwork特性.=>有的话注册拦截器=>在拦截器中通过using包裹原有方法代码,并执行=>最后看是否调用了Complete方法=>是的话Savechanges

启动流程

private AbpBootstrapper([NotNull] Type startupModule, [CanBeNull] Action<AbpBootstrapperOptions> optionsAction = null)
{
Check.NotNull(startupModule, nameof(startupModule)); var options = new AbpBootstrapperOptions();
optionsAction?.Invoke(options); if (!typeof(AbpModule).GetTypeInfo().IsAssignableFrom(startupModule))
{
throw new ArgumentException($"{nameof(startupModule)} should be derived from {nameof(AbpModule)}.");
} StartupModule = startupModule; IocManager = options.IocManager;
PlugInSources = options.PlugInSources; _logger = NullLogger.Instance; if (!options.DisableAllInterceptors)
{
AddInterceptorRegistrars();
}
}

在AddAbp创建abpBootsstrapper时,会对执行这个ctor函数,可以看到最后一行有个AddInterceptorRegistrars

这里就是注册所有的拦截器

private void AddInterceptorRegistrars()
{
ValidationInterceptorRegistrar.Initialize(IocManager);
AuditingInterceptorRegistrar.Initialize(IocManager);
EntityHistoryInterceptorRegistrar.Initialize(IocManager);
UnitOfWorkRegistrar.Initialize(IocManager);
AuthorizationInterceptorRegistrar.Initialize(IocManager);
}

其中UnitOfWorkRegistrar.Initialize(IocManager);就是注册工作单元拦截器

internal static class UnitOfWorkRegistrar
{
/// <summary>
/// Initializes the registerer.
/// </summary>
/// <param name="iocManager">IOC manager</param>
public static void Initialize(IIocManager iocManager)
{
// 添加组件注册事件
iocManager.IocContainer.Kernel.ComponentRegistered += (key, handler) =>
{
var implementationType = handler.ComponentModel.Implementation.GetTypeInfo();
// 根据unitofwork特性注册
HandleTypesWithUnitOfWorkAttribute(implementationType, handler);
// 按照约定注册
HandleConventionalUnitOfWorkTypes(iocManager, implementationType, handler);
};
} private static void HandleTypesWithUnitOfWorkAttribute(TypeInfo implementationType, IHandler handler)
{
// IsUnitOfWorkType:如果给定类型实现有unitofwork返回true
// AnyMethodHasUnitOfWork:给定类型的成员有unitofwork返回tue
// 这里只做了一件事 如果当前类型有Unitofwork特性。则会注册拦截器
if (IsUnitOfWorkType(implementationType) || AnyMethodHasUnitOfWork(implementationType))
{
handler.ComponentModel.Interceptors.Add(new InterceptorReference(typeof(UnitOfWorkInterceptor)));
}
} private static void HandleConventionalUnitOfWorkTypes(IIocManager iocManager, TypeInfo implementationType, IHandler handler)
{
// IUnitOfWorkDefaultOptions:用于设置/获取工作单元的默认选项 (范围\超时\隔离等级等)
// 这里是判断ioc容器中有没有注册 IUnitOfWorkDefaultOptions 防止后面获取不到出异常
if (!iocManager.IsRegistered<IUnitOfWorkDefaultOptions>())
{
return;
}
// 从ioc容器中获取 IUnitOfWorkDefaultOptions
var uowOptions = iocManager.Resolve<IUnitOfWorkDefaultOptions>();
// 当前类型是否是 约定的类型,是的话注册拦截器
// IRepository,IApplicationService实现了这两个的会注册拦截器
if (uowOptions.IsConventionalUowClass(implementationType.AsType()))
{
handler.ComponentModel.Interceptors.Add(new InterceptorReference(typeof(UnitOfWorkInterceptor)));
}
} private static bool IsUnitOfWorkType(TypeInfo implementationType)
{
return UnitOfWorkHelper.HasUnitOfWorkAttribute(implementationType);
} private static bool AnyMethodHasUnitOfWork(TypeInfo implementationType)
{
return implementationType
.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Any(UnitOfWorkHelper.HasUnitOfWorkAttribute);
}
}

实现原理

工作单元拦截器

AbpBootstraper创建之后会执行工作单元拦截器的注册. 下面看看注册的拦截器长什么样子的

internal class UnitOfWorkInterceptor : IInterceptor
{
private readonly IUnitOfWorkManager _unitOfWorkManager;
private readonly IUnitOfWorkDefaultOptions _unitOfWorkOptions; public UnitOfWorkInterceptor(IUnitOfWorkManager unitOfWorkManager, IUnitOfWorkDefaultOptions unitOfWorkOptions)
{
_unitOfWorkManager = unitOfWorkManager;
_unitOfWorkOptions = unitOfWorkOptions;
} /// <summary>
/// 拦截方法
/// </summary>
public void Intercept(IInvocation invocation)
{
MethodInfo method;
try
{
method = invocation.MethodInvocationTarget;
}
catch
{
method = invocation.GetConcreteMethod();
}
// 工作单元特性
var unitOfWorkAttr = _unitOfWorkOptions.GetUnitOfWorkAttributeOrNull(method);
// 如果没有工作单元这个特性,直接执行原方法里的代码
if (unitOfWorkAttr == null || unitOfWorkAttr.IsDisabled)
{
invocation.Proceed();
return;
} // 这里分异步和同步执行的,
PerformUow(invocation, unitOfWorkAttr.CreateOptions());
} 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))
{
// 执行原方法内部代码
// 如果没有出错,就会执行Complete方法
invocation.Proceed();
uow.Complete();
}
} 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,// Task actualReturnValue,
async () => await uow.CompleteAsync(),// Func<Task> postAction
exception => uow.Dispose()// Action<Exception> finalAction
);
}
// 如果异步方法返回值是Task<TResult>
else
{
invocation.ReturnValue = InternalAsyncHelper.CallAwaitTaskWithPostActionAndFinallyAndGetResult(
invocation.Method.ReturnType.GenericTypeArguments[0],// Type taskReturnType
invocation.ReturnValue,// object actualReturnValue
async () => await uow.CompleteAsync(),// Func<Task> action
exception => uow.Dispose()//Action<Exception> finalAction
);
}
}
}
}

下面先看看同步的uow方法:同步的没什么好说的,就是开启一个工作单元,或者可以说开启了一个事务.

执行内部的方法,执行没有错误的情况下,会调用Complete()方法. Complete方法等下在讲.

下面看看实际情况工作单元下类之间的方法是怎么调用的.

public class InvoiceService
{
private readonly InvoiceCoreService _invoiceCoreService;
public InvoiceService(InvoiceCoreService invoiceCoreService)
{
_invoiceCoreService=InvoiceCoreService;
} public bool Invoice(InvoiceDto input)
{
return _invoiceCoreService.InvoiceElectronic(input);
}
}
public class InvoiceCoreService:ITransientDependency
{
public readonly ThridPartyService _thridPartyService;
public InvoiceCoreService(ThridPartyService thridPartyService)
{
_thridPartyService=thridPartyService;
}
[UnitOfWork]
public bool InvoiceElectronic(InvoiceDto input)
{
var res= _thridPartyService.Run(input);
Console.WriteLine("调用ThirdParty完成");
return res;
}
}
public class ThridPartyService:ITransientDependency
{
[UnitOfWork]
public bool Run(InvoiceDto input)
{
Console.WriteLine("调百旺开电子票");
return true;
}
}

这是我工作中的例子,首先要做的就是模拟开电子票

InvoiceService会调用InvoiceCoreService,InvoiceCoreService会调用ThirdPartyService.

那么执行的过程是怎么样的呢?

public bool Invoice(InvoiceDto Input)
{
using(var uow=_unitOfWrokManager.Begin(options))
{
bool res=false;
using(var uow2=_unitOfWrokManager.Begin(options))
{
res= ThridPartyService.Run();
uow2.Complete();
return res;
}
// invoiceCoreService
Console.WriteLine("调用ThirdParty完成");
Uow.Complete();
}
}

两个工作单元是用using嵌套的 ,只要代码执行失败了,就会导致Complete不会调用.

而 Complete() 方法没有执行,则会导致 uow 对象被释放的时候

uow.Dispose() 内部检测到 Complete() 没有被调用,Abp 框架也会自己抛出异常

下面看下异步方法

首先是没有返回值的情况也就是返回值是Task

public static async Task AwaitTaskWithPostActionAndFinally(
Task actualReturnValue,
Func<Task> postAction,
Action<Exception> finalAction)
{
Exception exception = null; try
{
// 执行原方法
await actualReturnValue;
await postAction();// async () => await uow.CompleteAsync() 提交更改
}
catch (Exception ex)
{
exception = ex;
throw;
}
finally
{
// exception => uow.Dispose() 最后都会执行uow的释放方法
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 });
}
public static async Task<T> AwaitTaskWithPostActionAndFinallyAndGetResult<T>(Task<T> actualReturnValue, Func<Task> postAction, Action<Exception> finalAction)
{
Exception exception = null; try
{
// 执行原方法内部代码 并获取返回值
var result = await actualReturnValue;
// 执行CompleteAsync方法
await postAction();
return result;
}
catch (Exception ex)
{
exception = ex;
throw;
}
finally
{
// Dispose方法
finalAction(exception);
}
}

工作单元拦截器就到此为止了

IUnitOfWorkManager

拦截器中有_unitOfWorkManager.Begin,之前只是说了 是开启了一个工作单元,那么这个是什么呢,我们来看看吧.

 internal class UnitOfWorkManager : IUnitOfWorkManager, ITransientDependency
{
private readonly IIocResolver _iocResolver;
private readonly ICurrentUnitOfWorkProvider _currentUnitOfWorkProvider;
private readonly IUnitOfWorkDefaultOptions _defaultOptions; public IActiveUnitOfWork Current
{
get { return _currentUnitOfWorkProvider.Current; }
} public UnitOfWorkManager(
IIocResolver iocResolver,
ICurrentUnitOfWorkProvider currentUnitOfWorkProvider,
IUnitOfWorkDefaultOptions defaultOptions)
{
_iocResolver = iocResolver;
_currentUnitOfWorkProvider = currentUnitOfWorkProvider;
_defaultOptions = defaultOptions;
} public IUnitOfWorkCompleteHandle Begin()
{
return Begin(new UnitOfWorkOptions());
} public IUnitOfWorkCompleteHandle Begin(TransactionScopeOption scope)
{
return Begin(new UnitOfWorkOptions { Scope = scope });
} public IUnitOfWorkCompleteHandle Begin(UnitOfWorkOptions options)
{
// 没有设置参数 填充默认参数
options.FillDefaultsForNonProvidedOptions(_defaultOptions);
// 获取当前工作单元
var outerUow = _currentUnitOfWorkProvider.Current;
// 当前已有工作单元,直接创建一个在它内部的工作单元
if (options.Scope == TransactionScopeOption.Required && outerUow != null)
{
return new InnerUnitOfWorkCompleteHandle();
}
// 如果没有外部的工作单元,从ioc容器中直接获取一个
var uow = _iocResolver.Resolve<IUnitOfWork>();
// 绑定外部工作单元的一些事件.
// 调用Complete方法后 设置当前工作单元为null
uow.Completed += (sender, args) =>
{
_currentUnitOfWorkProvider.Current = null;
};
// 失败的时候,设置当前工作单元为null
uow.Failed += (sender, args) =>
{
_currentUnitOfWorkProvider.Current = null;
};
// 从ioc容器释放
uow.Disposed += (sender, args) =>
{
_iocResolver.Release(uow);
}; // 设置过滤器
if (outerUow != null)
{
options.FillOuterUowFiltersForNonProvidedOptions(outerUow.Filters.ToList());
}
// 调用UnitOfWorkBase的Begin方法
uow.Begin(options); // 设置租户id
if (outerUow != null)
{
uow.SetTenantId(outerUow.GetTenantId(), false);
}
// 绑定外部工作单元为刚才创建的工作单元
_currentUnitOfWorkProvider.Current = uow; return uow;
}
}
}

可以看到返回值是IUnitOfWorkCompleteHandle

public interface IUnitOfWorkCompleteHandle : IDisposable
{
void Complete();
Task CompleteAsync();
}

他有个默认实现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 Task CompleteAsync()
{
_isCompleteCalled = true;
return Task.FromResult(0);
} 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的时候把_isCompleteCalled设置为true,在Dispose的时候判断,如果没有调用Complete那么会抛出异常

那么这里仅仅是做个标记的作用,savachanges 并没有在这里调用,那么数据库的保存是什么时候执行的呢?

其实之前在UnitOfManager内部的工作单元的类型就是InnerUnitOfWorkCompleteHandle,那么外部的工作单元是从Ioc容器中获取的.IUnitOfWork

它有几个默认实现,其中就有EfCoreUnitOfWork。里面就有savechanges方法. 它继承自UnitOfWorkBase。 UnitOfWorkBase继承自IUnitOfWork

public interface IUnitOfWork : IActiveUnitOfWork, IUnitOfWorkCompleteHandle
{
/// <summary>
/// 工作单元唯一Id,
/// </summary>
string Id { get; } /// <summary>
/// 外部工作单元的引用对象
/// </summary>
IUnitOfWork Outer { get; set; }
}
public abstract class UnitOfWorkBase:IUnitOfWork
{
// 其他代码 略.
// 由具体的子类实现
protected abstract void CompleteUow(); public void Complete()
{
// 确保Complete方法没有被调用过
PreventMultipleComplete();
try
{
CompleteUow();
_succeed = true;
OnCompleted();
}
catch (Exception ex)
{
_exception = ex;
throw;
}
}
}
public class EfCoreUnitOfWork : UnitOfWorkBase, ITransientDependency
{
// 遍历所有有效的dbcontext,依次调用SaveChanges方法
public override void SaveChanges()
{
foreach (var dbContext in GetAllActiveDbContexts())
{
SaveChangesInDbContext(dbContext);
}
}
}

ABP工作单元的更多相关文章

  1. 基于DDD的.NET开发框架 - ABP工作单元(Unit of Work)

    返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...

  2. 手工搭建基于ABP的框架 - 工作单元以及事务管理

    一个业务功能往往不只由一次数据库请求(或者服务调用)实现.为了功能的完整性,我们希望如果该功能执行一半时出错,则撤销前面已执行的改动.在数据库层面上,事务管理实现了这种完整性需求.在ABP中,一个完整 ...

  3. ABP(现代ASP.NET样板开发框架)系列之12、ABP领域层——工作单元(Unit Of work)

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之12.ABP领域层——工作单元(Unit Of work) ABP是“ASP.NET Boilerplate Pr ...

  4. ABP框架 - 工作单元

    文档目录 本节内容: 简介 在ABP中管理连接和事务 约定的工作单元 UnitOfWork 特性 IUnitOfWorkManager 工作单元详情 禁用工作单元 非事务性工作单元 工作单元方法调用另 ...

  5. ABP理论学习之工作单元(Unit of Work)

    返回总目录 本篇目录 公共连接和事务管理方法 ABP中的连接和事务管理 仓储类 应用服务 工作单元 工作单元详解 关闭工作单元 非事务的工作单元 工作单元方法调用其它 工作单元作用域 自动保存 IRe ...

  6. 解析ABP框架中的事务处理和工作单元,ABP事务处理

    通用连接和事务管理方法连接和事务管理是使用数据库的应用程序最重要的概念之一.当你开启一个数据库连接,什么时候开始事务,如何释放连接...诸如此类的. 正如大家都知道的,.Net使用连接池(connec ...

  7. ABP的工作单元

    http://www.aspnetboilerplate.com/Pages/Documents/Unit-Of-Work 工作单元位于领域层.   ABP的数据库连接和事务处理: 1,仓储类 ASP ...

  8. ABP领域层——工作单元(Unit Of work)

    ABP领域层——工作单元(Unit Of work) 点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之12.ABP领域层——工作单元(Unit Of work) ...

  9. ABP官方文档翻译 3.6 工作单元

    工作单元 介绍 ABP中的连接和事务管理 传统的工作单元方法 控制工作单元 UnitOfWork特性 IUnitOfWorkManager 工作单元详情 禁用工作单元 无事务工作单元 一个工作单元方法 ...

随机推荐

  1. poj 1011 :Sticks (dfs+剪枝)

    题意:给出n根小棒的长度stick[i],已知这n根小棒原本由若干根长度相同的长木棒(原棒)分解而来.求出原棒的最小可能长度. 思路:dfs+剪枝.蛮经典的题目,重点在于dfs剪枝的设计.先说先具体的 ...

  2. 通过YUM命令查找对应命令是通过何种软件提供的

    # yum whatprovides [命令]

  3. html5 pc端参考网址

    http://huodong.baidu.com/zhuanpan/?SEM&PC&refer=107255

  4. Js的cookie

    我们现在清除cookie缓存. 然后发起的请求观察: ============== ============= 已经有cookie的情况下发起请求: 我们在看看第一次没有缓存的情况下,返回的数据:

  5. javascript 跨域问题解决办法总结

    跨域的意思就是不同域名之间的页面默认是无法通信的.因为浏览器默认是禁止跨域的: 图所示:chrome浏览器尝试获取mainFrame失败,提示DomException 1).假如你有个网站 a.com ...

  6. JavaScript 编程模式

    编程模式,是源自经验和探索总结出的最佳实践方案,既有助于可读性和可维护性,也有助于提升整体性能. 行为隔离 总则:结构.样式和行为之间两两隔离. 避免在结构中使用内联事件 尽量少用 <scrip ...

  7. [转载] Java开发在线编辑Word同时实现全文检索

    一.背景介绍 Word文档与日常办公密不可分,在实际应用中,当某一文档服务器中有很多Word文档,假如有成千上万个文档时,用户查找打开包含某些指定关键字的文档就变得很困难,一般情况下能想到的解决方案是 ...

  8. 27 python 网络基础之网络协议

    一.操作系统基础 操作系统:(Operating System,简称OS)是管理和控制计算机硬件与软件资源的计算机程序,是直接运行在“裸机”上的最基本的系统软件,任何其他软件都必须在操作系统的支持下才 ...

  9. fastCGI模块

    这个模块允许nginx同FastCGI协同工作,并且控制哪些参数将被安全传递.例: location / { fastcgi_pass localhost:9000; fastcgi_index in ...

  10. javascript网页复制功能-复制到粘贴板-兼容多数浏览器(不使用flash)

    使用方法:clipBordCopy("hello Copy");//执行后复制hello Copy到粘贴板 通过 var result = clipBordCopy("h ...