使用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的开发者必须掌握的技术!

posted @ 2018-06-13 09:22 szdlsoft 阅读(...) 评论(...) 编辑 收藏