继承自AbpController的一个方法报“Abp.AbpException: Did not call Complete method of a unit of work.”的异常,解决办法,在该方法上加上特性[UnitOfWork(IsDisabled = true)],也就是对该方法禁用工作单元。
abp版本:4.5
原因分析:
由于所有的AbpController的方法都默认是工作单元方法,而工作单元方法在发生异常时都会触发实现了IUnitOfWorkCompleteHandle接口的类的Dispose()方法。abp中实现了IUnitOfWorkCompleteHandle接口的类有两个,一个是抽象类UnitOfWorkBase,它是所有实现事务控制的类的父类,它里面实现了除了事务控制逻辑以外的其他逻辑,比如Dispose();另一个是类InnerUnitOfWorkCompleteHandle,它是一个非事务的UnitOfWorkCompleteHandle,不会创建真实的事务。
当我们的方法代码包含事务时(对数据库实体的增、删、改),abp会创建包含事务的IUnitOfWorkCompleteHandle实现类,也就是UnitOfWorkBase的子类,当这个方法出现异常时就会触发UnitOfWorkBase的Dispose()方法,从而抛出异常信息,源码如下:
当我们的方法不包含事务时(对数据实体只有查询操作或者根本没有数据库的操作),abp会创建InnerUnitOfWorkCompleteHandle,当这个方法出现异常时就会触发InnerUnitOfWorkCompleteHandle的Dispose()方法,但是他的Dispose()里面的代码 HasException()有bug,如下图
即使我们的代码有异常,它还返回的是false,导致它执行了代码
throw new AbpException(DidNotCallCompleteMethodExceptionMessage);
也就是说它屏蔽了我们的所有其他的真实异常信息,不管出现什么异常,都抛出这么个异常信息。
所以当我们的方法没有包含事务时,我们禁用工作单元,那么abp不会创建InnerUnitOfWorkCompleteHandle类,出现异常时也就不会执行那部分代码逻辑,这样我们就能看到真正的异常信息了。还有就是据说abp的Core版本,也就是vNext不存在这个bug,当然能我没有测试,感兴趣的同行可以使用vNext试试,或者新项目的话就不要使用以前的abp了,直接使用vNext更好。
Tips:
VS2017Enterprise 调试源码的设置:
参考资料: