使用ABP框架踩過的坑系列4
數據庫連接和事務管理,是數據庫應用中的最重要概念之一。做過的人,都會頭疼:何時Open一個連接?何時Start一個事務?何時Dispose這個連接?... ABP框架試圖用一個叫做UnitOfWork的模型來解決這些。實際開發中,引入UnitOfWork,同時也會帶來一些坑。
[UnitOfWork] public void SaveFoodMaterials( FoodMaterialItem food,FoodMaterialCategory cat) { FoodMaterial fm = Mapper.Map<FoodMaterial>(food); fm.FoodMaterialCategory= GetCategory(cat.Name); ; fm = FoodMaterialRepository.Insert(fm); foreach( var t in food.Nutritions) { ImportNutrition(fm, t); } }
這個會拋Exception: DBContext已經dispose了! [UnitOfWork]沒起作用,攔截器沒起作用?其實UnitOfWork還有三種使用方式:過程式、慣例、聲明式
過程式
using (var unitOfWork = _unitOfWorkManager.Begin()) { 。。。 unitOfWork.Complete(); }
過程式, 只要能Resolve UnitOfWorkManager, 就能Run
聲明式
[UnitOfWork] public void SomeMethod( ) { ... }
聲明式, 這種就不一定能Run了!
namespace Abp.Domain.Uow { /// <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; //利用Castle這個IOC的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))); } } } }
internal static class UnitOfWorkHelper { /// <summary> /// Returns true if UOW must be used for given type as convention. /// </summary> /// <param name="type">Type to check</param> public static bool IsConventionalUowClass(Type type) //慣例 { return typeof(IRepository).IsAssignableFrom(type) || typeof(IApplicationService).IsAssignableFrom(type); } /// <summary> /// Returns true if given method has UnitOfWorkAttribute attribute. /// </summary> /// <param name="methodInfo">Method info to check</param> public static bool HasUnitOfWorkAttribute(MemberInfo methodInfo) //聲明 { return methodInfo.IsDefined(typeof(UnitOfWorkAttribute), true); } ... }
過程、慣例,都沒問題,聲明式是要註意的:
UnitOfWork Attribute Restrictions 聲明式的限制
You can use UnitOfWork attribute for;
- All public or public virtual methods for classes those are used over interface (Like an application service used over service interface).
- All public virtual methods for self injected classes (Like MVC Controllers and Web API Controllers).
- All protected virtual methods.
It‘s suggested to always make the method virtual. You can not use it for private methods. Because, ASP.NET Boilerplate uses dynamic proxying for that and private methods can not be seen by derived classes. UnitOfWork attribute (and any proxying) does not work if you don‘t use dependency injection and instantiate the class yourself.
聲明式的這些限制,其實是由攔截器的實現機制引起的,ABP的攔截器是用Castle DynamicProxy 動態代理來做的,動態代理是在運行時生成(使用.Net emit)一個新類(繼承於原類或接口),攔截的method, 都是用override來插入代碼的, 所以只能支持Interface或Virtual的方法。
總結:ABP中大量使用了AOP(面向切面編程),分離了橫切關註點:Authorization, Validation, Exception Handling, Logging, Localization, Database Connection Management, Setting Management, Audit Logging;實現機理是用動態代理做的攔截器, 作為開發者對這個機理的徹底了解,有助於我更好的使用框架,也有助於用類似的方法做我們自己的AOP,畢竟AOP是我輩熱衷於OOP的開發者必須掌握的技術!
使用ABP框架踩過的坑系列4