ABP工作单元
简介
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工作单元的更多相关文章
- 基于DDD的.NET开发框架 - ABP工作单元(Unit of Work)
返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...
- 手工搭建基于ABP的框架 - 工作单元以及事务管理
一个业务功能往往不只由一次数据库请求(或者服务调用)实现.为了功能的完整性,我们希望如果该功能执行一半时出错,则撤销前面已执行的改动.在数据库层面上,事务管理实现了这种完整性需求.在ABP中,一个完整 ...
- ABP(现代ASP.NET样板开发框架)系列之12、ABP领域层——工作单元(Unit Of work)
点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之12.ABP领域层——工作单元(Unit Of work) ABP是“ASP.NET Boilerplate Pr ...
- ABP框架 - 工作单元
文档目录 本节内容: 简介 在ABP中管理连接和事务 约定的工作单元 UnitOfWork 特性 IUnitOfWorkManager 工作单元详情 禁用工作单元 非事务性工作单元 工作单元方法调用另 ...
- ABP理论学习之工作单元(Unit of Work)
返回总目录 本篇目录 公共连接和事务管理方法 ABP中的连接和事务管理 仓储类 应用服务 工作单元 工作单元详解 关闭工作单元 非事务的工作单元 工作单元方法调用其它 工作单元作用域 自动保存 IRe ...
- 解析ABP框架中的事务处理和工作单元,ABP事务处理
通用连接和事务管理方法连接和事务管理是使用数据库的应用程序最重要的概念之一.当你开启一个数据库连接,什么时候开始事务,如何释放连接...诸如此类的. 正如大家都知道的,.Net使用连接池(connec ...
- ABP的工作单元
http://www.aspnetboilerplate.com/Pages/Documents/Unit-Of-Work 工作单元位于领域层. ABP的数据库连接和事务处理: 1,仓储类 ASP ...
- ABP领域层——工作单元(Unit Of work)
ABP领域层——工作单元(Unit Of work) 点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之12.ABP领域层——工作单元(Unit Of work) ...
- ABP官方文档翻译 3.6 工作单元
工作单元 介绍 ABP中的连接和事务管理 传统的工作单元方法 控制工作单元 UnitOfWork特性 IUnitOfWorkManager 工作单元详情 禁用工作单元 无事务工作单元 一个工作单元方法 ...
随机推荐
- Hibernate异常_01
1. 在使用 Hibernate(ojdbc14.jar[1536554字节,Win7显示大小为1501KB]) 操作 Oracle10g(32位)的时候,出现如下 error: INFO: HHH0 ...
- 【转】ORACLE的数据类型
原文;http://linjian004.bokee.com/3916067.html 常用的数据库字段类型如下: 字段类型 中文说明 限制条件 其它说明 CHAR 固定长度字符串 最大长度2000 ...
- virualbox andirodx86
背景 谷歌提供的andriod sdk模拟器在windows平台上很卡,是因为sdk是针对arm处理器架构的(就是嵌入式),而我们的windows系统是 x86架构 或者是 AMD架构,所以安卓模拟器 ...
- PowerCmd 2.2 注册码
PowerCmd 是共享软件,现分享注册码一枚: 用户名:Sub 注册码:PCMDA-86128-PCMDA-70594 来源:http://blog.csdn.net/subchen/article ...
- windows7安装PyQt5(通过pip install 安装)
开始接触PyQt5 ,总结了一下安装的方法 默认各位已经安装好了Python环境 首先,确定一下之前没有安装过pyqt5,如果安装了,可以先卸载,避免出现意外, 之前装了几次没成功就是这种情况,卸载命 ...
- Rainmeter如何打开控制面板的小程序
控制面板功能都是通过访问cpl文件来关联它们的,假设你的系统盘在C盘,那么它们的本地在C:\Windows\System32\ Rainmeter通过使用这个应用程序C:\Windows\System ...
- listening 1
It was regrettable that such great issues had to be the thrust and parry of a general election. But ...
- mysql1130远程连接没有权限的解决方法
网上查了半天,终于解决 远程连接没有权限的原因有两种,一个是因为mysql的限制,一个是防火墙的限制. ,解决防火墙限制: 在mysql服务主机上将防火墙关闭或者在防火墙高级设置里面加入出入站规则,加 ...
- 3.16 draw 3.17 更新函数
3.16 draw virtual void draw(); void HelloWorld::draw() { CCSize size = CCDirector::sharedDirector()- ...
- 通过ifreme实现文件上传
模板页面添加ifreme <div style=' display: none;' > <iframe name ="uploadResponse_attachm ...