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. 听翁恺老师mooc笔记(16)--程序设计与C语言

    问题1:计算机遍布生活的各个方面,若你需要一个功能可以下载APP,我们需要的大部分功能都可以找到对应的APP,如果没有可以自己写一个软件,但是很少人需要这么做,那么我们为什么学习计算机编程语言? 学习 ...

  2. Beta版本敏捷冲刺每日报告——Day3

    1.情况简述 Beta阶段第三次Scrum Meeting 敏捷开发起止时间 2017.11.4 08:00 -- 2017.11.4 22:00 讨论时间地点 2017.11.4晚9:00,软工所实 ...

  3. 《Language Implementation Patterns》之 数据聚合符号表

    本章学习一种新的作用域,叫做数据聚合作用域(data aggregate scope),和其他作用域一样包含符号,并在scope tree里面占据一个位置. 区别在于:作用域之外的代码能够通过一种特殊 ...

  4. JAVA接口基础知识总结

    1:是用关键字interface定义的. 2:接口中包含的成员,最常见的有全局常量.抽象方法. 注意:接口中的成员都有固定的修饰符. 成员变量:public static final     成员方法 ...

  5. 关于collectionView和tableView的两种cell的出列方法的区别

    相信好多人一定会对collectionView和tableView的两种cell出列方法有所疑问,下面以UICollection为例子进行举例说明 假设我们已经创建了一个collectionView, ...

  6. RE:1054652545 - 论自己是如何蠢死的

    1.Java web 项目中 login/list 文件夹中return "login/list" 反复读取不到对应的jsp文件 一周后检查出来的原因上一级文件夹login前面多出 ...

  7. 使用 VSCode 编写 .NET Core 项目之初体验

    注:本文在根据 微软官方文档指导下,根据自己的学习中整理,并不完全照搬文档,但也大体和文档学习路线相似,主要为记录学习过程. 官方学习地址: https://code.visualstudio.com ...

  8. 原生JS封装时间运动函数

    /*讲时间运动之前先给大家复习一下运动函数 通常大家都会写运动框架,一个定时器(Timer),一个步长(step 就是每次运动的距离),一个当前位置(current)一个目标位置(target),然后 ...

  9. python中两种方法实现二分法查找,细致分析二分法查找算法

    之前分析了好多排序算法,可难理解了呢!!(泣不成声)这次我要把二分查找总结一下,这个算法不算难度特别大,欢迎大家参考借鉴我不喜欢太官方的定义,太晦涩的语言,让人看了就头晕.我希望加入我自己的理解,能帮 ...

  10. Python之几种常用模块

    模块 注意事项: 所有的模块导入都应该尽量往上写 内置模块 扩展模块 自定义模块 模块不会重复被导入 : sys.moudles 从哪儿导入模块 : sys.path import import 模块 ...