ABP中UnitOfWorkRegistrar攔截器是整個ABP中非常關鍵的一個部分,這個部分在整個業務系統中也是用的最多的一個部分,這篇文章的主要思路並不是寫如何使用ABP中的UnitOfWork,重點在於分析整個ABP框架中是如何實現工作單元的,如果想了解如何快速使用ABP中的UnitOfWork,請讀下面的文章,這個是ABP的官方文檔。
整個過程的分析還是按照之前系列攔截器中的順序進行說明,首先是從AbpBootstrapper中的構造函數開始說起。
/// <summary>
/// Creates a new <see cref="AbpBootstrapper"/> instance.
/// </summary>
/// <param name="startupModule">Startup module of the application which depends on other used modules. Should be derived from <see cref="AbpModule"/>.</param>
/// <param name="optionsAction">An action to set options</param>
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();
}
}
如果我們沒有在初始化的時候配置DisableAllInterceptors=true的情況下,默認是開啟所有的攔截器的,然后在AddInterceptorRegistrars()方法中會執行UnitOfWorkRegistrar.Initialize(IocManager)方法,從而開始了整個UnitOfWorkRegistrar的初始化操作,那么讓我們一起進入到UnitOfWorkRegistrar這個靜態類中去分析整個過程吧!
/// <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 += (key, handler) =>
{
var implementationType = handler.ComponentModel.Implementation.GetTypeInfo();
HandleTypesWithUnitOfWorkAttribute(implementationType, handler);
HandleConventionalUnitOfWorkTypes(iocManager, implementationType, handler);
};
}
private static void HandleTypesWithUnitOfWorkAttribute(TypeInfo implementationType, IHandler handler)
{
if (IsUnitOfWorkType(implementationType) || AnyMethodHasUnitOfWork(implementationType))
{
handler.ComponentModel.Interceptors.Add(new InterceptorReference(typeof(UnitOfWorkInterceptor)));
}
}
private static void HandleConventionalUnitOfWorkTypes(IIocManager iocManager, TypeInfo implementationType, IHandler handler)
{
if (!iocManager.IsRegistered<IUnitOfWorkDefaultOptions>())
{
return;
}
var uowOptions = iocManager.Resolve<IUnitOfWorkDefaultOptions>();
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);
}
}
就像這個類最開始的說明一樣,這個類的作用是為了用於需要Unit Of Work機制的類注冊攔截器的,在這個類的Initialize方法中,首先會注入整個ABP系統中唯一的IIocManager,然后就是訂閱唯一的IocContainer這個容器的ComponentRegistered事件,在訂閱事件中首先是獲取當前觸發此事件的類型信息,接下來就是執行HandleTypesWithUnitOfWorkAttribute這個方法,這個方法中主要是判斷當前方法是否定義了UnitOfWork這個自定義屬性,如果當前方法定義了這個自定義UnitOfWork屬性的話,那么就注冊UnitOfWorkInterceptor這個攔截器,后面一個HandleConventionalUnitOfWorkTypes這個方法我們可以看看里面的一些關鍵過程,這里面核心的是調用了Abp.Domain.Uow命名空間下面的靜態類UnitOfWorkDefaultOptionsExtensions中的IsConventionalUowClass方法,在這個方法中又會去調用UnitOfWorkDefaultOptions : IUnitOfWorkDefaultOptions這個類中的 public List<Func<Type, bool>> ConventionalUowSelectors { get; }這個委托List,從而確定當前類型是否需要注入UnitOfWorkInterceptor這個攔截器,執行到這里最關鍵的就是ConventionalUowSelectors 這個委托的List到底默認添加了哪些類型的委托?我們來看看這個類的初始化過程。
public UnitOfWorkDefaultOptions()
{
_filters = new List<DataFilterConfiguration>();
IsTransactional = true;
Scope = TransactionScopeOption.Required;
IsTransactionScopeAvailable = true;
ConventionalUowSelectors = new List<Func<Type, bool>>
{
type => typeof(IRepository).IsAssignableFrom(type) ||
typeof(IApplicationService).IsAssignableFrom(type)
};
}
看完了這個你應該明白了,ABP中會默認為繼承自IRepository或者是IApplicationService的兩種類型添加UnitOfWork特性,看完了這個你肯定在想如果自己也需要添加自定義的規則那該怎么辦?這里ABP中強大的Configuration就起大作用了,這里只需要在繼承自AbpModule類的PreInitialize方法中加入下面的代碼就可以了。
Configuration.UnitOfWork.ConventionalUowSelectors.Add(type => ...);
至此所有支持UnitOfWork特性的類型及方法我們就都理解了,至少我們知道ABP中默認為哪些方法或類型添加UnitOfWork特性已經自己如何自定義一些規則使我們的代碼能夠使用這個特性。
后面一個部分就是深入理解UnitOfWorkInterceptor這個攔截器了,還是和前面一樣,我們來看看這個類中到底寫了些什么?
/// <summary>
/// This interceptor is used to manage database connection and transactions.
/// </summary>
internal class UnitOfWorkInterceptor : IInterceptor
{
private readonly IUnitOfWorkManager _unitOfWorkManager;
private readonly IUnitOfWorkDefaultOptions _unitOfWorkOptions;
public UnitOfWorkInterceptor(IUnitOfWorkManager unitOfWorkManager, IUnitOfWorkDefaultOptions unitOfWorkOptions)
{
_unitOfWorkManager = unitOfWorkManager;
_unitOfWorkOptions = unitOfWorkOptions;
}
/// <summary>
/// Intercepts a method.
/// </summary>
/// <param name="invocation">Method invocation arguments</param>
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)
{
//No need to a uow
invocation.Proceed();
return;
}
//No current uow, run a new one
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))
{
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,
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()
);
}
}
}
首先也是看這個類的注釋:This interceptor is used to manage database connection and transactions.顧名思義就是這個攔截器是為了管理數據庫的連接和事物操作的,在這個類的構造函數中默認注入了兩個關鍵的類型,IUnitOfWorkManager和IUnitOfWorkDefaultOptions這兩個一個用於管理UnitOfWork,另一個用於配置UnitOfWork,在我們的系統中,當我們調用帶UnitOfWork特性的方法時,首先就會將該方法掛起,然后執行對應的攔截器的Intercept(IInvocation invocation)這個方法,在這個方法中首先會判斷當前執行方法是否定義過自定義的UnitOfWork屬性或者是符合特定規則的類型,關於這個類型上面也已經提到過,這里不再贅述僅僅看看其內部實現原理。
public static UnitOfWorkAttribute GetUnitOfWorkAttributeOrNull(this IUnitOfWorkDefaultOptions unitOfWorkDefaultOptions, MethodInfo methodInfo)
{
var attrs = methodInfo.GetCustomAttributes(true).OfType<UnitOfWorkAttribute>().ToArray();
if (attrs.Length > 0)
{
return attrs[0];
}
attrs = methodInfo.DeclaringType.GetTypeInfo().GetCustomAttributes(true).OfType<UnitOfWorkAttribute>().ToArray();
if (attrs.Length > 0)
{
return attrs[0];
}
if (unitOfWorkDefaultOptions.IsConventionalUowClass(methodInfo.DeclaringType))
{
return new UnitOfWorkAttribute(); //Default
}
return null;
}
通過這個方法我們就能夠獲取當前方法的自定義的UnitOfWorkAttribute屬性,在獲取了這個屬性后,我們還需要判斷該UnitOfWorkAttribute中的IsDisabled是否為true,如果為true的話那么就讓該方法執行不再執行后面的一些操作了,所以這里ABP也給我們提供了一種不使用UnitOfWork特性的方法那就是定義UnitOfWorkAttribute的IsDisable屬性,具體怎么使用呢?請看下面的示例。
[UnitOfWork(IsDisabled = true)]
public virtual void RemoveFriendship(RemoveFriendshipInput input)
{
_friendshipRepository.Delete(input.Id);
}
在執行完這些判斷的過程以后就是最關鍵的PerformUow這個方法了,在這個方法中會根據當前方法是否是異步方法分為兩個過程,這里我僅僅使用同步的方法來進行說明這個過程到底是怎么樣的?
private void PerformSyncUow(IInvocation invocation, UnitOfWorkOptions options)
{
using (var uow = _unitOfWorkManager.Begin(options))
{
invocation.Proceed();
uow.Complete();
}
}
這個方法有沒有非常熟悉,這個就是我們需要將我們執行的方法包裝在一個Begin和一個Complete方法中,從而來完成整個工作單元的過程,在ABP中的官方文檔中也提到了這種加入工作單元的方式,這里也可以看看這個示例。
public class MyService
{
private readonly IUnitOfWorkManager _unitOfWorkManager;
private readonly IPersonRepository _personRepository;
private readonly IStatisticsRepository _statisticsRepository;
public MyService(IUnitOfWorkManager unitOfWorkManager, IPersonRepository personRepository, IStatisticsRepository statisticsRepository)
{
_unitOfWorkManager = unitOfWorkManager;
_personRepository = personRepository;
_statisticsRepository = statisticsRepository;
}
public void CreatePerson(CreatePersonInput input)
{
var person = new Person { Name = input.Name, EmailAddress = input.EmailAddress };
using (var unitOfWork = _unitOfWorkManager.Begin())
{
_personRepository.Insert(person);
_statisticsRepository.IncrementPeopleCount();
unitOfWork.Complete();
}
}
}
這個就是采用注入IUnitOfWorkManager的方式來完成工作單元的操作的,但是個人還是建議沒有特別的目的還是自定義UnitOfWork屬性比較好。在下一篇我會深入到UnitOfWorkManager中去看這個Begin和Complete方法到底做了些什么?需要關注請點擊這里。
最后,點擊這里返回整個ABP系列的主目錄。
