仓储定义:在领域层和数据映射层的中介,使用类似集合的接口来存取领域对象
实际上,仓储被用于领域对象在数据库上的操作(实体Entity和值对象Value types).一般来说,我们针对不同的实体或聚合根Aggregate Root会创建相对的仓储。
IRepository接口
在ABP中,仓储类要实现IRepository接口。最好的方式是针对不同仓储对象定义各自不同的接口。
针对Person实体的仓储接口声明的示例如下所示
Public interface IPersonRepository:IRepository<Person>{}
IPersonRepository继承自IRepository<TEntity>,用来定义Id的类型为int的实体。如果你的实体Id数据类型不是int,你可以继承IRepository<TEntity,TPrimaryKey>接口,如下所示
public interface IPersonRepository:IRepository<Person,long>{}
对于仓储类,IRepository定义了许多泛型的方法。比如:Select ,Insert ,Update ,Delete方法(CRUD)。在大多数的时候,这些方法已足够应付一般实体的需要。如果这些方法对于实体来说已足够,我们便不需要再去创建这个实体所需的仓储接口/类。在Implementation章节有更多细节
(1)查询(Query)
IRepository定义了从数据库中检索实体的常用方法
取单一实体(Getting single entity)
Get方法被用于根据主键值(Id)取得对应的实体。当数据库中根据主键值找不到相符合的实体时,它会抛弃出例外。Single方法类似Get方法,但是它的输入参数是一个表达式而不是主键值(Id)。因此,我们可以使用Lambda表达式来取得实体,示例如下
var person=_personRepository.Get(42);
var person=_personRepository.Single(p=>o.Name=="");
注意,Single方法会在给出的条件找不到实体或符合的实体超过一个以上时,都会抛出例外。
FirstOrDefault也是一样,但是当没有符合Lambda表达式或Id的实体时,会回传null(取代抛出异常)。当有超过一个以上的实体符合条件,他智慧返回一个实体。
Load并不会从数据库中检索实体,但它会创建延迟执行所需的代理对象。如果你只会使用Id属性,实际上并不会检索实体,它只有在你存取想要查询实体的某个属性时才会从数据库中查询实体。当有性能需求的时候,这个方法可以代替Get方法。Load方法在EF与ABP的整合中也有实现。如果ORM提供者(Provider)没有实现这个方法,Load方法运行的会和Get方法一样。
ABP有些方法具有异步(Async)版本,可以应用异步开发模型
取得实体列表(Getting list of entities)
GetAll返回IQueryable<T>类型的对象。因此我们可以在调用完这个方法之后进行Linq操作
var query=from person in _personRepository.GetAll();
where person.IsActive
orderby person.Name select person;
var people=query.ToList();
List<Person> personlist=_personRepository.GetAll().Where(p=>p.Name.Contains("H")).OrderBy(p=>p.Name).Skip(40).Take(20).Tolist();
说明:关于IQueryable<T>当你调用GetAll这个方法在Repository对象以外的地方,必定会开启数据库连接。这是因为IQueryable<T>允许延迟执行。它会直到你调用Tolist方法或在forEach循环上(或是一些存取已查询的对象方法)使用IQueryable<T>时,才会实际执行数据库查询。因此,当你调用ToList方法时,数据库连接必须是启用状态。我们可以使用ABP所提供的UnitOfWork特性在调用的方法上实现。注意,Application Sservice方法预设都已经是UnitOfWork。因此,使用了GetAll方法就不需要如同Application Service的方法上添加UnitOfWork特性。
仓储的实现
ABP在设计上是采用不指定特定ORM框架或其他存取数据库技术的方式。只需要实现IRepository接口,任何框架都可以使用。
public
class
PersonAppService : IPersonAppService
{
private readonly IRepository<Person> _personRepository;
public PersonAppService(IRepository<Person> personRepository)
{
_personRepository = personRepository;
}
public void CreatePerson(CreatePersonInput input)
{
person = new Person { Name = input.Name, EmailAddress = input.EmailAddress };
_personRepository.Insert(person);
}
}
{
private readonly IRepository<Person> _personRepository;
public PersonAppService(IRepository<Person> personRepository)
{
_personRepository = personRepository;
}
public void CreatePerson(CreatePersonInput input)
{
person = new Person { Name = input.Name, EmailAddress = input.EmailAddress };
_personRepository.Insert(person);
}
}
PersonAppService的构建注入了IRepository<Person>并且使用其Insert方法。当你有需要为实体创建一个存储方法。
管理数据库链接
数据库连接的开启和关闭,在仓储方法中,ABP会自动化的进行连接管理。
当仓储方法被调用后,数据库连接会自动开启并且启动事务,当仓储方法执行结束并且返回以后,所有的实体变化都会被存储,事务被提交并且数据库链接被关闭,一切都由ABP自动话管理控制。如果仓储方法抛出异常,事务会自动地回滚并且数据库连接会被关闭,上述所有操作在实现了IRepository接口的仓储类所有公开的方法中都可以被调用。
如果仓储方法调用其它仓储方法(即便是不同仓储的方法),他们共享一个连接和事务。连接会由仓储方法调用链最上层那个仓储方法所管理。