数据库连接和事务管理,是数据库应用中的最重要概念之一。做过的人,都会头疼:何时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的更多相关文章

  1. ABP框架踩过的坑系列6

    ABP框架踩过的坑系列6 应是无事.齐侯方才的确到了吴纠庭院https://www.mixcloud.com/ltTFvU888smi8jS/几日行军劳顿其实齐侯本应该睡下了https://www.m ...

  2. 使用ABP框架踩过的坑系列1

        企业级(例如ERP)应用, 一遍一遍的在重复:认证.验证.异常处理.日志.国际化和本地化.数据库连接管理.配置管理. 审计记录等,同时.NET有很多最佳实践:分层.模块化.DDD领域驱动.DI ...

  3. 使用ABP框架踩过的坑系列5

    DDD领域驱动开发,实际是为复杂的业务场景而生的,为了让开发人员专注于业务,而操作系统.数据库.网络之类的技术细节,必须要持久透明化:实际就是数据库系统DBMS的ORM抽象,目标就是业务不需要考虑数据 ...

  4. 使用ABP框架踩过的坑系列3

    从架构角度来讲,ApplicationService究竟应该如何定位,一种说法是直接对应用例UseCase, 也就是直接对应UI, 这个UI是广义的,不仅仅是浏览器的页面,也包括API调用.还是从我曾 ...

  5. 使用ABP框架踩过的坑系列2

    ABP中有很多惯例,如果使用得当,可以事半功倍,如果使用不当,也会有很大的麻烦,是否适当其实还是要看Need需求 ASP.NET Boilerplate (ABP) is an open source ...

  6. ABP框架踩坑记录

    ABP框架踩坑记录 ASP.NET Boilerplate是一个专用于现代Web应用程序的通用应用程序框架. 它使用了你已经熟悉的工具,并根据它们实现最佳实践. 文章目录 使用MySQL 配置User ...

  7. Abp框架之执行Update-Database 命令系列错误

    废话不多说,直接开门见山.首先的 第一个错误:一般都是,碰到这个问题不要慌,先不要急着去查看sql服务是否开启,首先按F5启动项目,报错之后直接终止项目,然后再执行Update-Database命令 ...

  8. 谈谈出入React框架踩过的坑

    1 在JSX的元素中写入内联样式,例如<div style={"color:blue"}></div> 报错:warning:Style prop valu ...

  9. 踩过的坑系列之InputStream.read(byte[])方法

    项目之前都是好好的,最近现场那边出现一个问题,报错不是合法的json字符串,这个json字符串是通过http请求访问获得的. 通过直接在浏览器上直接访问http这个请求,发现返回的json也是完全正确 ...

随机推荐

  1. 源码包的解压 .tar.gz /.tar.bz2的解压

    解压方式如下: .tar.gz     格式解压为          tar   -zxvf   xx.tar.gz .tar.bz2   格式解压为          tar   -jxvf    ...

  2. Java List/HashSet/HashMap的排序

    在对Java无序类集合,如List(ArrayList/LinkedList).HashSet(TreeSet有序).HashMap等排序时,Java中一个公共的类Collections,提供了对Ja ...

  3. 转)TNS协议--翻译自《The Oracle Hackers Handbook》

    在开发源工具解决Oracle中的问题时,必须了解TNS协议.在努力理解TNS协议的时候,Oracle JDBC驱动程序(classes12.zip)会是一个很有用的资源TNS头. 每个TNS包都有一个 ...

  4. Py2exe——将python程序变成windows下可执行的exe

    一.安装Py2exe 二.定义一个目录,把你的Python文件放在下面,如为AddFileRandom.py文件 然后新建一个go.py文件,放于相同目录下,内容为: from distutils.c ...

  5. IIS PHP Warning: Unknown: open(c:\\php\\tmp\\sess_xxx, O_RDWR) failed: Permission denied (13) in Unknown on line 0

    出现这个问题的原因是你修改了php保存session的路径,但没有给用户访问该目录的权限.   右键该目录,给users用户组读.写的权限.

  6. 设置 svn 与 web线上同步

    默认你已经配置好了svn服务 1.假设我们的线上网站目录为:/data/www/xxx 2.假设svn的仓库目录为:/data/svn/repo 一.checkout一份svn到线上网站目录 svn ...

  7. 为什么二代测序的原始数据中会出现Read重复现象?

    为什么二代测序的原始数据中会出现Read重复现象? 要搞清楚这个read重复(duplicate)的问题,我想我们需要从NGS数据的产出过程说起,具体来说如下: 基因组DNA提取: DNA随机打断,最 ...

  8. Python和JavaScript间代码转换4个工具-乾颐堂

    Python 还是 JavaScript?虽然不少朋友还在争论二者目前谁更强势.谁又拥有着更为光明的发展前景,但毫无疑问,二者的竞争在 Web 前端领域已经拥有明确的答案.立足于浏览器平台,如果放弃 ...

  9. linux 硬盘分区攻略

    以下的sdX代表硬盘分区(如sda1,sda2,sdb1...等等),如果已有的硬盘分区需要改变大小的话,请参考另一篇文章. /boot:开机用的磁盘空间了,至少78MB,一般给100MB就好了. / ...

  10. Hadoop 1: NCDC 数据准备

    本文介绍Hadoop- The Definitive Guide一书中的NCDC数据准备,为后面的学习构建大数据环境; 环境 3节点 Hadoop 2.7.3 集群; java version &qu ...