Abp Uow 设计
初始化入口
在AbpKernelModule类中,通过UnitOfWorkRegistrar.Initialize(IocManager) 方法去初始化

/// <summary>
/// This class is used to register interceptor for needed classes for Unit Of Work mechanism.
/// </summary>
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 += ComponentRegistered;
} private static void ComponentRegistered(string key, IHandler handler)
{
if (UnitOfWorkHelper.IsConventionalUowClass(handler.ComponentModel.Implementation))
{
//Intercept all methods of all repositories.
handler.ComponentModel.Interceptors.Add(new InterceptorReference(typeof(UnitOfWorkInterceptor)));
}
else if (handler.ComponentModel.Implementation.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Any(UnitOfWorkHelper.HasUnitOfWorkAttribute))
{
//Intercept all methods of classes those have at least one method that has UnitOfWork attribute.
//TODO: Intecept only UnitOfWork methods, not other methods!
handler.ComponentModel.Interceptors.Add(new InterceptorReference(typeof(UnitOfWorkInterceptor)));
}
}
}
UnitOfWorkRegistrar
UnitOfWorkInterceptor 拦截器
基于Castle.Core的AOP动态拦截

区分同步异步,通过UowManager开启事务
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();
}
}
PerformUow
invocation.Proceed();会嵌套执行,将Uow嵌套包含,嵌套的Uow不会单独再开启事务,通过InnerUnitOfWorkCompleteHandle标识,全部完成后complete,提交事务(UnitOfWorkDefaultOptions默认开启事务)
UnitOfWorkManager
UnitOfWorkManager 继承IUnitOfWorkManager
/// <summary>
/// Unit of work manager.
/// Used to begin and control a unit of work.
/// </summary>
public interface IUnitOfWorkManager
{
/// <summary>
/// Gets currently active unit of work (or null if not exists).
/// </summary>
IActiveUnitOfWork Current { get; } /// <summary>
/// Begins a new unit of work.
/// </summary>
/// <returns>A handle to be able to complete the unit of work</returns>
IUnitOfWorkCompleteHandle Begin(); /// <summary>
/// Begins a new unit of work.
/// </summary>
/// <returns>A handle to be able to complete the unit of work</returns>
IUnitOfWorkCompleteHandle Begin(TransactionScopeOption scope); /// <summary>
/// Begins a new unit of work.
/// </summary>
/// <returns>A handle to be able to complete the unit of work</returns>
IUnitOfWorkCompleteHandle Begin(UnitOfWorkOptions options);
}
IUnitOfWorkManager

在Begin方法中根据option的设置,创建了一个新的Uow,并设置了Uow相应的Completed,Failed,Disposed的方法。

CallContextCurrentUnitOfWorkProvider
这里有必要提一下CallContextCurrentUnitOfWorkProvider 的对象,他继承ICurrentUnitOfWorkProvider
CallContextCurrentUnitOfWorkProvider的主要功能其实只有一个:通过current返回当前UOW环境下的UOW实例。
一般思路是:将IUnitOfWork对象定义为实例变量或者是类变量。 但是两者事实上都不可行。
如果定义为类变量,那就会面临线程安全的问题,解决方式无非加锁,但会导致并发能力下降,ABP是web框架,因为锁导致并发能力下降是不能接受的。
如果定义为实例变量,在同一线程其他地方resolve CallContextCurrentUnitOfWorkProvider这个实例的时候都会得到一个新的实例,新的实例下current自然是NULL.
ABP的做法是:线程逻辑上下文+线程安全的Dictinoray容器。
线程逻辑上下文用于存储UOW实例的key, 而线程逻辑上下文对于本线程是全局可访问的,而同时具有天然的隔离性。这就确保了当前线程的各个地方都可以得到current的UOW的key
线程安全的Dictinoray容器是一个类实例,用于存放UOW的实例,通过UOW的key就可以取到UOW的实例。(引用: http://www.cnblogs.com/1zhk/p/5309043.html)
这里有两篇CallContext的博文,推荐看一下
UnitOfWork
1.UnitOfWorkBase
接下来,分析下UnitOfWork是如何封装事务的。
基于接口隔离原则的考量,ABP作者将UnitOfWork的方法分到了三个不同的接口中,如下图。
IUnitOfWorkCompleteHandle:定义了UOW同步和异步的complete方法。实现UOW完成时候的逻辑。
IActiveUnitOfWork:一个UOW除了以上两个接口中定义的方法和属性外,其他的属性和方法都在这个接口定义的。比如Completed,Disposed,Failed事件代理,Filter的enable和disable,以及同步、异步的SaveChanges方法。
IUnitOfWork:继承了上面两个接口。定义了外层的IUnitOfWork的引用和UOW的begin方法。 ABP是通过构建一个UnitOfWork的链,将不同的方法纳入到一个事务中。
UnitOfWorkBase:这个抽象类实现了上面三个接口中定义的方法,而真正实现事务控制的方法是由这个抽象类的子类实现的(比如,真正创建TransactionScope的操作是在EfUnitOfWork,NhUnitOfWork这样的之类中实现的)。UOW中除了事务控制逻辑以外的逻辑都是由UnitOfWorkBase抽象类实现的。
/// <summary>
/// Defines a unit of work.
/// This interface is internally used by ABP.
/// Use <see cref="IUnitOfWorkManager.Begin()"/> to start a new unit of work.
/// </summary>
public interface IUnitOfWork : IActiveUnitOfWork, IUnitOfWorkCompleteHandle
{
/// <summary>
/// Unique id of this UOW.
/// </summary>
string Id { get; } /// <summary>
/// Reference to the outer UOW if exists.
/// </summary>
IUnitOfWork Outer { get; set; } /// <summary>
/// Begins the unit of work with given options.
/// </summary>
/// <param name="options">Unit of work options</param>
void Begin(UnitOfWorkOptions options);
}
IUnitOfWork
UnitOfWorkBase中的Begin实现如下:
public void Begin(UnitOfWorkOptions options)
{
if (options == null)
{
throw new ArgumentNullException("options");
} PreventMultipleBegin(); //通过_isBeginCalledBefore 字段bool判断是否已经begin
Options = options; //TODO: Do not set options like that, instead make a copy? SetFilters(options.FilterOverrides); //通过设置过滤器达到全局数据过滤的效果,在ef的实现中,通过引用EntityFramework.DynamicFilter实现 BeginUow();
}
2.开始UnitOfWork
CompleteUow和BeginUow 在UowBase中为抽象方法,具体实现在efUow中,稍后分析
/// <summary>
/// Should be implemented by derived classes to complete UOW.
/// </summary>
protected abstract void CompleteUow();
3.Complete
Complete方法在UnitOfWorkInterceptor拦截中,PerformSyncUow方法内,执行完invocation.Proceed();会调用Complete方法。
/// <inheritdoc/>
public void Complete()
{
PreventMultipleComplete(); //通过_isCompleteCalledBefore字段Bool判断是否已经Complete,保证只执行一次
try
{
CompleteUow();
_succeed = true;
OnCompleted(); //调用完成的事件,在UnitOfWorkManager中设置,当前的UnitOfWork为null
}
catch (Exception ex)
{
_exception = ex;
throw;
}
}
4.Dispose
/// <inheritdoc/>
public void Dispose()
{
if (IsDisposed)
{
return;
} IsDisposed = true; if (!_succeed) //在Complete是会设置_succeed,没有成功则执行Faild事件,会将当前的UnitOfWord设为null
{
OnFailed(_exception);
} DisposeUow(); //为抽象方法,在子类中实现
OnDisposed(); //OnFailed和OnDisposed均在UnitOfWordManage中设置
}
EfUnitOfWork
1.BeginUow
protected override void BeginUow()
{
if (Options.IsTransactional == true)
{
var transactionOptions = new TransactionOptions
{
IsolationLevel = Options.IsolationLevel.GetValueOrDefault(IsolationLevel.ReadUncommitted),
}; if (Options.Timeout.HasValue)
{
transactionOptions.Timeout = Options.Timeout.Value;
} CurrentTransaction = new TransactionScope( //开启事务,并给定默认为Required
Options.Scope.GetValueOrDefault(TransactionScopeOption.Required),
transactionOptions,
Options.AsyncFlowOption.GetValueOrDefault(TransactionScopeAsyncFlowOption.Enabled)
);
}
}
2.CompleteUow
protected override void CompleteUow()
{
SaveChanges(); //遍历EfContent,调用SaveChange方法
if (CurrentTransaction != null) //如果存在事务则执行
{
CurrentTransaction.Complete();
}
}
Abp Uow 设计的更多相关文章
- ABP架构设计交流群-上海线下交流会的内容分享(有高清录像视频的链接)
点这里进入ABP系列文章总目录 ABP架构设计交流群-7月18日上海线下交流会内容分享 因为最近工作特别忙,很久没有更新博客了,真对不起关注我博客和ABP系列文章的朋友! 原计划在7月11日举行的AB ...
- ABP模块设计
ABP模块设计 返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术 ...
- ABP分层设计
ABP分层设计 一.为什么要分层 分层架构是所有架构的鼻祖,分层的作用就是隔离,不过,我们有时候有个误解,就是把层和程序集对应起来,就比如简单三层架构中,在你的解决方案中,一般会有三个程序集项目:XX ...
- 基于DDD的.NET开发框架 - ABP分层设计
返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...
- 持续提升程序员幸福指数——使用abp vnext设计一款面向微服务的单体架构
可能你会面临这样一种情况,在架构设计之前,你对业务不甚了解,需求给到的也模棱两可,这个时候你既无法明确到底是要使用单体架构还是使用微服务架构,如果使用单体,后续业务扩展可能带来大量修改,如果使用微服务 ...
- 基于DDD的.NET开发框架 - ABP模块设计
返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...
- ABP 初探 之 权限设计
大.小项目都要设计权限,都想设计一个通用的权限,把权限做的比较复杂,现在了解了ABP的设计思路,觉得设计很简单,但实现方法与思路耐人寻味. 本篇只介绍AbpPermissions的数据库设计,其它表结 ...
- ABP 初探 之User、Role、Permission数据库设计 (EntityFramework 继承的另一种使用方法)
最近群里(134710707)的朋友都在讨论ABP源码,我把最近学习的内容记录下来,同时也分享给大家,希望正在研究ABP源码的朋友有一定帮助. 上篇介绍ABP的多语言,本篇主要介绍权限的数据库设计,用 ...
- User、Role、Permission数据库设计ABP
ABP 初探 之User.Role.Permission数据库设计 (EntityFramework 继承的另一种使用方法) 最近群里(134710707)的朋友都在讨论ABP源码,我把最近学习的内容 ...
随机推荐
- android BaseAdapter getView 理解
ListView是安卓中很经常使用的一个控件. 安卓设计使用Adapter来对ListView进行管理. 可是系统提供的Adapter无法满足一些复杂的显示情况,这个时候我们就须要使用BaseAdap ...
- map area 标签的使用
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...
- jquery衬衣产品内容详情页
html代码: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www ...
- python操作word(改课文格式)【最终版】
python操作word的一些方法,前面写了一些感悟,有点跑题,改了下题目,方便能搜索到.心急的可以直接拉到最后看代码,我都加了比较详细的注释. 从8.3号早上9点,到8.8号下午5点半下班,终于把这 ...
- mysql 位运算
& 与运算 | 或运算 ^ 异或运算 或者 你也可以将 与运算理解为 + 法 例如 1|2 = 3 (1+2 = 3)1|2|4 = 7 (1+2+4 = 7) 将 异或运算理解为 - ...
- [usb]usb otg和host
USB OTG 设备既能做主机,又能做设备.USB HOST是指主机.当OTG 插到 HOST 上,OTG 的角色 就是 device.当device 插到 OTG 上,OTG 的角色就是 HOST. ...
- PHP——转义字符
链接:百度-转义字符 http://baike.baidu.com/link?url=obfdOqATx4TO0Ev_kFnPz37wwW3SDhFPsvNobVTidhFuCn2zK5VmCuW1L ...
- 使用shell读取文本文件发送到kafka
#!/bin/sh ## 参数定义 dt=`date +"%Y%m%d" -d "-1 days"` outpath=/xxxx_log_${dt}.txt b ...
- Redis list 之增删改查
一.增加 1.lpush [lpush key valus...] 类似于压栈操作,将元素放入头部 127.0.0.1:6379> lpush plist ch0 ch1 ch2 (integ ...
- KMP + 求最小循环节 --- HDU 1358 Period
Period Problem's Link: http://acm.hdu.edu.cn/showproblem.php?pid=1358 Mean: 给你一个字符串,让你从第二个字符开始判断当前长度 ...