在ABP框架中,仓储,服务,这块算是最为重要一块之一了.ABP框架提供了创建和组装模块的基础,一个模块能够依赖于另一个模块,一个程序集可看成一个模块,
一个模块可以通过一个类来定义这个模块,而给定义这个类要继承自已经疯转好的AbpModule..net通过反射来获取这些程序集中的类或者方法
模块的调用往往涉及到先后顺序,如果模块A依赖于模块B,那么模块B要在模块A之前初始化,初始化就相当于注册,如使用IocManager对登记类进行注册,
上面这个方法我们就把MyModule1 注入到MyModule2中了,在调用MyModule2的时候可以初始化MyModule1 .
什么是依赖注入呢?百科是这样说:“依赖注入是一种软件设计模式,一个或多个依赖项(或服务)被注入或通过引用传递到一个依赖对象,并且成为客户端状态的一部分。这种模式把客户端依赖项的创建从它自己的行为中分离出来,允许程序设计成松耦合的,遵循依赖倒置和单一职责的原则。和服务定位器模式相比,它允许客户端知道他们使用的系统查找依赖项。”
不使用依赖注入技术,很难管理依赖项和发布一个结构良好的应用。
假设我们有一个应用程序服务,使用仓储(repository)类插入实体到数据库。在这种情况下,应用程序服务类依赖于仓储(repository)类,如下
UserService使用UserRepository插入Person到数据库。但是这段代码有一些问题:
1,服务层UserService通过接口IUserRepository调用CreatePerson实现新增一个User对象,看似调用了IUserRepository接口,但是实际上还是依赖于仓促层的UserRepository.
2,UserService通过IUserRepository创建对象的时候,实际上new一个UserRepository区实现,这与直接调用UserRepository无差别,IUserRepository失去存在的意义.
3,如果未来我们需要修改UserRepository类,但是UserService依赖于它,这时候,我们需要修改所有依赖UserRepository的类.
4,有了这样的依赖,很难对UserService进行单元测试。
5,与"高内聚低耦合"的的原则背道而驰,这里可以看到服务层与仓储层有依赖.
为了解决这些问题于是就有了下面这个版本.
这就是工厂模式,实际上在abp之前我也经常用这种方式,非常繁琐,搭框架老是出错,UserRepositoryFactory是一个静态类,创建并返回一个IUserRepository
UserService服务不需要直接去创建UserRepository.
这种方法虽然可以,但是依然存在一些问题.
1,UserService依然依赖于UserRepositoryFactory
2,每一个仓储都有写一个工厂,很繁琐.
3,测试性还是不好.
解决办法有几种,包括属性注入,构造函数注入,和依赖注入框架等等.
上面就是abp中构造函数注入与属性输入的完美运用.现在,UserService不知道哪些类实现userRepository以及如何创建它。谁需要使用UserService,首先创建一个IUserServiceUserService并将其传递给构造函数就可以了.
有人可能说userRepository的从属类里面可能存在依赖,依赖注入框架自动化管理依赖关系都已经解决了这些问题.构造函数注入模式是一个完美的提供类的依赖关系的方式。通过这种方式,你不能创建类的实例,而不提供依赖项。它也是一个强大的方式显式地声明是什么类的需求正确地工作。
但是,在某些情况下,该类依赖于另一个类,但也可以没有它。这通常是适用于横切关注点(如日志记录)。一个类可以没有工作日志,但它可以写日志如果你提供一个日志对象。在这种情况下,您可以定义依赖为公共属性,而不是让他们放在构造函数,上面例子中NullLogger.Instance 是一个单例对象,实现了ILogger接口,但实际上什么都没做(不写日志。它实现了ILogger实例,且方法体为空),在我们需要写日志的地方,我们只需要UserService.Logger = new Log4NetLogger();如此,我们就可以写入日志了,如果不写就不调用,因此是一个可选的依赖.
几乎所有的依赖注入框架都支持属性注入模式
ABP的依赖注入基于 Castle Windsor框架。Castle Windsor最成熟的DI框架之一。依赖注入的框架还有好多,如Unity,Ninject,StructureMap,Autofac等,之前我用过Unity其他的几个没有研究过,依赖框架都可以自动解决依赖关系。他们可以创建所有依赖项(递归地依赖和依赖关系)。所以你只需要根据注入模式写类和类构造函数&属性,其他的交给DI框架处理!在良好的应用程序中,类甚至独立于DI框架。整个应用程序只会有几行代码或类,显示的与DI框架交互。
有人说上面这个例子看不出来依赖注入啊,其实这里UserService是继承自IUserService,而IUserService继承自IApplicationService,abp在IApplicationService封装了很多东西,ABP会自动注册它,因为它实现IApplicationService接口(它只是一个空的接口)。它会被注册为transient (每次使用都创建实例)。当你注入(使用构造函数注入)IUserService接口成一个类,UserService对象会被自动创建并传递给构造函数。
命名约定在这里非常重要。例如你可以将名字PersonAppService改为 MyPersonAppService或另一个包含“PersonAppService”后缀的名称,由于IPersonAppService包含这个后缀。但是你可以不遵循PeopleService命名您的服务类。如果你这样做,它将不会为IPersonAppService自动注册(它需要自注册(self-registration)到DI框架,而不是接口),所以,如果你想要你应该手动注册它.
仓储
上一章我们已经定义实体类,和DTOs,在仓储中可以直接调用,仓储是在领域层和数据映射层的中介,使用类似集合的接口来存取领域对象.
接口:
实现:
在例子中IRepository继承自abp已经封装好的IRepository<TEntity>中,在IRepository<TEntity>中已经为我们封装好了许多方法,这就省得我们在为每一个仓储创建不同的方法了,这点很好如下图.
包含了各式各样的方法,如增删查改等方法.还有一些Async的异步方法.GetAll返回IQueryable<T>类型的对象。因此我们可以在调用完这个方法之后进行Linq操作.
现在项目中运用的是EF框架,所以如果ORM框架没有提供Async的仓储方法则它会以同步的方式操作。同样地,举例来说,InsertAsync操作起来和EF的新增是一样的,因为EF会直到单元作业(unit of work)完成之后才会写入新实体到数据库中(DbContext.SaveChanges)。
数据库连接的开启和关闭,在仓储方法中,ABP会自动化的进行连接管理.当仓储方法被调用后,数据库连接会自动开启且启动事务。当仓储方法执行结束并且返回以后,所有的实体变化都会被储存, 事务被提交并且数据库连接被关闭,一切都由ABP自动化的控制。如果仓储方法抛出任何类型的异常,事务会自动地回滚并且数据连接会被关闭。上述所有操作在实现了IRepository接口的仓储类所有公开的方法中都可以被调用。如果仓储方法调用其它仓储方法(即便是不同仓储的方法),它们共享同一个连接和事务。连接会由仓储方法调用链最上层的那个仓储方法所管理。
另外所有的仓储对象都是暂时性的。这就是说,它们是在有需要的时候才会被创建。ABP大量的使用依赖注入,当仓储类需要被注入的时候,新的类实体会由注入容器会自动地创建.