1. 前言

在上一篇博文中 http://www.cnblogs.com/xiyin/p/6810350.html 我们讲到了ABP领域层的实体,这篇博文继续讲ABP的领域层,这篇博文的主题是ABP领域层—仓储。我们在上篇博文中介绍的ABP领域层的大致结构,在这篇文章就不一一赘述了。详情可以查看上篇博文。接下来直接进入主题。仓储的定义是这样的:

在领域层和数据映射层的中介,使用类似集合的接口来存取领域对象----Martin Fowler。

仓储被用于领域对象在数据库上的操作(实体Entity和值对象 Value types),一般来说,我们针对不同的实体(或聚合根Aggregate Root) 会创建相对应的仓储。

疑问:“那是不是对每个实体都创建相应的仓储呢?”

本文讲述的结构如下:

接下来,我将一一讲述。

2.ABP领域层—仓储

2.1 IRepository 接口


在ABP中,仓储类要实现IReposiotry接口。最好的方式是针对不同仓储对象定义各自不同的接口。

如果你的实体IDint 类型的,那么可以使用如下定义:

public  interface IPersonREpository : IRepository<Person>
{
//...
}

如果不是int 类型的,可以使用如下定义:

public interface IPersonRepository : IRepository<Person, long>
{
//...
}

对于仓储类,IRepository 定义了许多泛型的方法。比如Insert,Select,Update,Delete, 详情可以在源码的Abp.Domain.Repositories 程序集中详细查看。

接下来对 CRUD进行一一的介绍。

2.1.1 查询(Query)


*取得单一实体:

TEntity Get(TPrimaryKey id);
Task<TEntity> GetAsync(TPrimaryKey id);
TEntity Single(Expression<Func<TEntity,bool>> predicate);
TEntity FirstOrDefault(TPrimaryKey id);
Task<TEntity> FirstOrDefaultAsync(TPrimaryKey id); TEntity FirstOrDefault(Expression<Func<TEntity, bool>> predicate); Task<TEntity> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate); TEntity Load(TPrimaryKey id)

Get方法被用于根据主键值(Id)取得对应的实体。当数据库中根据主键值找不到相符合的实体时,它会抛出异常。

主要讲一下Single 方法的实例,因为参数是一个表达式:

var person = _personRepository.Get(42);
var person = _personRepository.Single(p => p.Name == "Robert");

Tip1:Single方法在给出的条件找不到实体或符合的实体超过一个以上时,都会抛出异常。

Tip2:FirstOrDefault也一样,但是当没有符合Lambda 表达式或Id的实体时,会返回null(取代抛出异常)。当有超过一个以上的实体符合条件,它只会返回第一个实体。

Tip3: Load 并不会从数据库中检索实体,但它会创建延迟执行所需的代理对象。如果你只使用Id 属性,实际上并不会检索实体,它只有在你存取想要查询实体的某个属性时才会从数据库中查询实体。

*取得实体列表:

List<TEntity> GetAllList(); Task<List<TEntity>> GetAllListAsync(); List<TEntity> GetAllList(Expression<Func<TEntity, bool>> predicate); Task<List<TEntity>> GetAllListAsync(Expression<Func<TEntity, bool>> predicate); IQueryable<TEntity> GetAll();

GetAllList 被用于从数据中检索所有实体,重载并且提供过滤实体的功能,

var allPeople = _personRepository.GetAllList();
var somePeople = _personRepository.GetAllList(person => person.IsActive && person.Age > 20 );

GetAll 返回IQueryable<T>类型的对象。因为是IQueryable 类型的,所以我们可以在调用完成后,进行Linq 操作。相关的Linq 操作函数,可以查看我的这篇博文。示例:

var query = from person in _personRepository.GetAll()
where person.IsActive
orderby person.Name
select person;
var people = query.ToList(); List<Person> personList2 = _personRepository.GetAll().Where(p =>p.Name.Contains("H")).OrderBy(p => p.Name).Skip(40).Take(20).ToList();

*自定义返回值

ABP 有一个额外的方法来实现IQueryable<T> 的延迟加载效果,而不需要在调用的方法上添加UnitOfWork 这个属性卷标。

T Query<T> (Func<IQueryable<TEnity>,T> queryMethod);

查询方法接受 Lambda (或一个方法)来接受IQueryabel<T> 并且返回任何对象类型。示例:

var person = _personRepository.Query(q => q.Name.Contains("H").OrderBy(p => p.Name).ToList());

2.1.2 新增


IRepository 接口定义了如下方法来新增一个实体到数据库。

TEntity Insert(TEntity entity); Task<TEntity> InsertAsync(TEntity entity); TPrimaryKey InsertAndGetId(TEntity entity); Task<TPrimaryKey> InsertAndGetIdAsync(TEntity entity); TEntity InsertOrUpdate(TEntity entity); Task<TEntity> InsertOrUpdateAsync(TEntity entity); TPrimaryKey InsertOrUpdateAndGetId(TEntity entity); Task<TPrimaryKey> InsertOrUpdateAndGetIdAsync(TEntity entity);

Tip: 新增方法会新增实体到数据库并且返回相同的已新增实体。InsertAndGetId 方法会返回新增实体的标识符(Id)。当我们采用自动递增标识符值且需要取得实体的新产生标志符值时非常好用。

2.1.3 更新


TEntity Update(TEntity entity);
Task<TEntity> UpdateAsync(TEntity entity)

2.1.4 删除


void Delete(TEntity entity);
Task DeleteAsync(TEntity entity);
void Delete(TPrimaryKey id);
Task DeleteAsync(TPrimaryKey id);
void Delete(Expression<Func<TEntity, bool>> predicate);
Task DeleteAsync(Expression<Func<TEntity, bool>> predicate);

2.1.5 其他方法


int  Count();
Task<int> CountAsync();
int Count(Expression<Func<TEntity, bool>> predicate);
Task<int> CountAsync(Expression<Func<TEntity, bool>> predicate); Long LongCount();
Task<long> LongCountAsync();
Long LongCount(Expression<Func<TEntity, bool>> predicate); Task<long> LongCountAsync(Expression<TEntity, bool>> predicate)

2.2 仓储的实现


ABP在设计上是采取不指定ORM框架或其它存取数据库技术的方法。只要实现IRepository 接口,任何框架都可以使用。

当你使用NHIbernateEntiyFramework,如果提供的方法已足够使用,你就不需要为你的实体创建仓储对象了。我们可以直接注入IRepository<TEntiy>IRepository<TEntity,TPrimaryKey> 下面实例为application service 使用仓储来新增实体到数据库:

public class PersonAppService : I PersonAppService
{
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);
}

2.3 管理数据库连接


数据库连接的开启和关闭,在仓储方法中,ABP会自动化的进行连接管理。

当仓储方法被调用后,数据库连接会自动开启且启动事务。当仓储方法执行结束并且返回以后,所有的实体变化都会被储存,事务被提交并且数据库连接被关闭,一切都由ABP自动化的控制。如果仓储方法抛出任何类型的异常,事务会自动地回滚并且数据连接会被关闭。

如果仓储方法调用其他仓储方法(即便是不同的仓储的方法),它们共享同一个连接和事务。连接会由仓储方法调用链最上层的那个仓储方法所管理。

2.4 仓储的生命周期


所有的仓储对象都是暂时性的,这就是说,它们是在由需要的时候才会被创建。ABP大量的使用依赖注入,当仓储类需要被注入的时候,新的类实体会由注入容器自动地创建。

2.5 仓储的最佳实践


  • 对于一个T类型的实体,是可以使用IRepository<T> 。但别任何情况下都创建定制化的仓储,除非我们真的很需要。预定于仓储方法已经足够应付各种案例。
  • 创建定制的仓储可以实现IRepository<TEntity>
  • 仓储类应该是无状态的。这意味着,你不该定义仓储等级的状态对象并且仓储方法的调用也不应该影响到其他调用。
  • 当仓储可以使用像 依赖注入,尽可能较少,或不根据其他服务。

3. 结语

正好让我回顾回顾之前阅读的书。虽说都是基础内容,但也加深了其理解,下一篇将会讲 工作单元。听说是最看好的。

ABP领域层知识回顾之---仓储的更多相关文章

  1. ABP领域层知识回顾之---工作单元

    1. 前言   在上一篇博文中(http://www.cnblogs.com/xiyin/p/6842958.html) 我们讲到了ABP领域层的仓储.这边博文我们来讲 工作单元.个人觉得比较重要.文 ...

  2. ABP领域层知识回顾之---实体

    标题:重温ABP领域层 1. 前言  最近一段时间一直在看<ABP的开发指南>(基于DDD的经典分层架构思想).因为之前一段时间刚看完<领域驱动设计:软件核心复杂性应对之道>, ...

  3. ABP(现代ASP.NET样板开发框架)系列之11、ABP领域层——仓储(Repositories)

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之11.ABP领域层——仓储(Repositories) ABP是“ASP.NET Boilerplate Proj ...

  4. ABP领域层——仓储(Repositories)

    ABP领域层——仓储(Repositories) 点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之11.ABP领域层——仓储(Repositories) ABP是 ...

  5. ABP领域层定义仓储并实现

    原文作者:圣杰 原文地址:ABP入门系列(3)——领域层定义仓储并实现 在原文作者上进行改正,适配ABP新版本.内容相同 一.先来介绍下仓储 仓储(Repository): 仓储用来操作数据库进行数据 ...

  6. ABP(现代ASP.NET样板开发框架)系列之12、ABP领域层——工作单元(Unit Of work)

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之12.ABP领域层——工作单元(Unit Of work) ABP是“ASP.NET Boilerplate Pr ...

  7. ABP(现代ASP.NET样板开发框架)系列之13、ABP领域层——数据过滤器(Data filters)

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之13.ABP领域层——数据过滤器(Data filters) ABP是“ASP.NET Boilerplate P ...

  8. ABP领域层——工作单元(Unit Of work)

    ABP领域层——工作单元(Unit Of work) 点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之12.ABP领域层——工作单元(Unit Of work) ...

  9. ABP领域层创建实体

    原文作者:圣杰 原文地址:ABP入门系列(2)——领域层创建实体 在原文作者上进行改正,适配ABP新版本.内容相同 这一节我们主要和领域层打交道.首先我们要对ABP的体系结构以及从模板创建的解决方案进 ...

随机推荐

  1. ORA-03206,当表空间不够时,如何以添加数据文件的方式扩展表空间

    准备导入一个数据库,大约为33G,开始创建的空库表空间为自增到20G,结果自然不够,然后就开始自动扩展表空间大小 使用的如下语句 --自动扩展表空间大小 ALTER DATABASE DATAFILE ...

  2. 201621123060《JAVA程序设计》第十三周学习总结

    1. 本周学习总结 以你喜欢的方式(思维导图.OneNote或其他)归纳总结多网络相关内容. 2. 为你的系统增加网络功能(购物车.图书馆管理.斗地主等)-分组完成 为了让你的系统可以被多个用户通过网 ...

  3. Ionic3的HTTP请求方法

    Ionic的http请求方法,一种是使用Ionic的Native的Http方法,另一种是使用Angular的Http请求方法. 第一种真的是看着文档都实现不了,很奇怪的错(官网文档:https://i ...

  4. Archlinux下i3wm与urxvt的配置

    前段时间学习了GitHub的两位前辈:Airblader和wlh320.他们的相关教程在https://github.com/Airblader/i3和https://github.com/wlh32 ...

  5. 第二十八条:利用有限制通配符来提升API的灵活性

    如第二十五条所述,参数化类型是不可变的.类型Type1和Type2而言,不管Type1与Type2的关系,List<Type1>既不是List<Type2>的子类型,也不是也不 ...

  6. Python扩展模块——自动化(testlinkAPI的使用)

    使用TESTLINKAPI首先要安装TestLink_API_Python_client-0.6.4(当前最新版本) 目前只使用到了通过api获取testlink中的自定义字段and值 url = ' ...

  7. mingw打dll ,lib包命令和调用

    1,下面的命令行将这个代码编译成 dll. gcc mydll.c -shared -o mydll.dll -Wl,--out-implib,mydll.lib 其中 -shared 告诉gcc d ...

  8. Linux知识积累(3)$()和${}和$(())和(())

    $()和${}和$(())和(()) $()和${}的用法:在 bash shell 中,$( ) 与 ` ` (反引号) 都是用来做命令替换用(command substitution)的.而 $( ...

  9. spring3——IOC之基于XML的依赖注入(DI )

    我们知道spring容器的作用是负责对象的创建和对象间关系的维护,在上一篇博客中我们讲到spring容器会先调用对象的无参构造方法创建一个空值对象,那么接下来容器就会对对象的属性进行初始化,这个初始化 ...

  10. Windows10+Docker搭建分布式Redis集群(一)

    摘要,Docker for Windows 仅支持专业版 目录 第一步:检查系统支持虚拟化 第二步:下载Docker对应版本 第三步:配置镜像加速 第一步:检查系统是否支持虚拟化 Docker前提是需 ...