积累被用来领域对象在数据库上的操作(实体

1. 前言

 在上一篇博文中 http://www.cnblogs.com/xiyin/p/6810350.html
大家讲到了ABP领域层的实业,那篇博文继续讲ABP的世界层,那篇博文的宗旨是ABP领域层—仓储。大家在上篇博文中介绍的ABP领域层的大意结构,在那篇小说就不1一赘述了。详细情形能够查阅上篇博文。接下来直接进去正题。仓储的定义是那样的:

在世界层和多少映射层的中介,使用类似集合的接口来存取领域对象—-马丁Fowler。

存款和储蓄被用来领域对象在数据库上的操作(实体Entity和值对象
Value types),一般的话,大家本着区别的实业(或聚合根Aggregate Root
会创制相呼应的仓库储存。

难点:“那是否对每一种实体都创造相应的贮存呢?”

正文讲述的构造如下:

图片 1

接下去,小编将次第讲述。

点那里进入ABP体系小说总目录

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举行每一个的介绍。

依靠DDD的今世ASP.NET开辟框架–ABP类别之1一、ABP领域层——仓库储存(Repositories)

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(替代抛出万分)。当有超越2个上述的实体符合条件,它只会回到第一个实体。

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)。当大家利用电动递增标记符值且要求得到实体的新发生标记符值时十一分好用。

ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称。

2.1.3 更新


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

ABP的官网http://www.aspnetboilerplate.com

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); 

ABP在Github上的开源项目https://github.com/aspnetboilerplate

2.一.伍 其余措施


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)

 

二.二 仓库储存的兑现


 ABP在安排上是选拔不内定ORM框架或此外存取数据库才具的点子。只要落成IRepository接口,任何框架都得以运用。

 当你使用NHIbernate
EntiyFramework,若是提供的格局已丰裕使用,你就不须求为你的实体创立仓库储存对象了。大家得以平素注入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.三 管理数据库连接


数据库连接的张开和关闭,在仓库储存方法中,ABP会自动化的拓展连接管理。

 当仓库储存方法被调用后,数据库连接会活动开启且运转职业。当仓库储存方法实行达成并且再次回到今后,全体的实体变化都会被贮存,事务被交付并且数据库连接被关闭,一切都由ABP自动化的垄断。若是存款和储蓄方法抛出任何项目标那么些,事务会自动地回滚并且数据连接会被关闭。

 假如存款和储蓄方法调用别的仓库储存方法(即就是见仁见智的囤积的不二等秘书籍),它们共享同1个连连和事情。连接会由仓库储存方法调用链最上层的不胜仓库储存方法所管理。

本文由台湾-小张提供翻译

二.肆 仓库储存的生命周期


 全数的蕴藏对象都以一时半刻的,那就是说,它们是在由索要的时候才会被创造。ABP大批量的运用依赖注入,当仓库储存类必要被注入的时候,新的类实体会由注入容器自动地创设。

存款和储蓄定义:“在世界层和数码映射层的中介,使用类似会集的接口来存取领域对象”(马丁Fowler)。

二.5 仓储的超级执行


  • 对此贰个T类型的实业,是足以采取IRepository<T>
    。但别任何意况下都成立定制化的存款和储蓄,除非大家真正很须要。预订于储存方法已经足够应付种种案例。
  • 创制定制的积累能够兑现IRepository<TEntity>
  • 储存类应该是无状态的。那象征,你不应当定义仓库储存等第的情状对象并且仓库储存方法的调用也不该影响到任何调用。
  • 当仓库储存能够采用像 重视注入,尽也许较少,或不遵照其他服务。

实际上,仓库储存被用来领域对象在数据库上的操作(实体Entity和值对象Value
types)。一般的话,我们本着不一致的实体(或聚合根Aggregate
Root)会成立相对应的积累。

3. 结语

 正好让笔者想起回看此前阅读的书。虽说都以基础内容,但也加深了其知晓,下1篇将会讲
职业单元。听别人说是最看好的。。哈哈。

IRepository接口 

在ABP中,仓储类要贯彻IRepository接口。最佳的法子是本着分化仓库储存对象定义各自分歧的接口。

本着Person实体的存款和储蓄接口表明的示范如下所示:

public interface IPersonRepository : IRepository<Person> 
{
}

IPersonRepository承继自IRepository<TEntity>,用来定义Id的档案的次序为int(Int3贰)的实业。假诺您的实业Id数据类型不是int,你能够承袭IRepository<TEntity,
TPrimaryKey>接口,如下所示:

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

对此仓库储存类,IRepository定义了过多泛型的不2秘技。举个例子:
Select,Insert,Update,Delete方法(CRUD操作)。在大部的时候,那几个办法已足已应付一般实体的内需。假若那几个方对于实体来讲已丰硕,大家便不须求再去创建那个实体所需的仓库储存接口/类。在Implementation章节有更加多细节。

4.参考文献

(1)查询(Query)

IRepository定义了从数据库中找找实体的常用方法。

赢得单1实体(Getting single entity)

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方法类似Get方法,不过它的输入参数是三个表达式而不是主键值(Id)。由此,大家得以写拉姆da表明式来获取实体。示举例下:

var  person = _personRepository.Get(42);
var  person = _personRepository.Single(p => o.Name == "Halil ibrahim Kalkan");

注意,Single方法会在交付的准绳找不到实体或符合的实体超越叁个上述时,都会抛出分裂。

FirstOrDefault也一律,不过当未有符合Lambda表达式或Id的实业时,会回传null(替代抛出尤其)。当有超越三个之上的实业符合条件,它只会回到第二个实体。

Load并不会从数据库中搜寻实体,但它会创设延迟进行所需的代办对象。尽管您只使用Id属性,实际上并不会招来实体,它只有在你存取想要查询实体的某部属性时才会从数据库中询问实体。当有品质供给的时候,这几个方法能够用来代表Get方法。Load方法在NHibernate与ABP的组合中也有落到实处。借使O奥迪Q7M提供者(Provider)未有落到实处这一个点子,Load方法运营的会和Get方法一样。

ABP有个别措施具备异步(Async)版本,可以利用在异步开拓模型上(见Async方法有关章节)。

获得实体列表(Getting list of entities)

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 = _personRespository.GetAllList();
var  somePeople = _personRepository.GetAllList(person => person.IsActive && person.Age > 42);

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> personList2 = _personRepository.GetAll().Where(p => p.Name.Contains("H")).OrderBy(p => p.Name).Skip(40).Take(20).ToList();

1经调用GetAll方法,那么大致全数查询都能够运用Linq实现。乃至能够用它来编排Join表明式。

说明:关于IQueryable<T>
当您调用GetAll这些措施在Repository对象以外的地点,必定会开启数据库连接。这是因为IQueryable<T>允许延迟试行。它会直到你调用ToList方法或在forEach循环上(或是一些存取已询问的靶子方法)使用IQueryable<T>时,才会实际推行数据库的查询。由此,当你调用ToList方法时,数据库连接必需是启用意况。我们能够利用ABP所提供的UnitOfWork天性在调用的法子上来落成。注意,Application
Service方法预设都曾经是UnitOfWork。因而,使用了GetAll方法就不需求就像Application
Service的主意上加多UnitOfWork天性。

多少措施具有异步版本,可选用在异步开拓模型(见关于async方法章节)。

自定义再次回到值(Custom return value)

ABP也有3个额外的艺术来贯彻IQueryable<T>的推移加载效果,而不供给在调用的格局上加多UnitOfWork这几个天性卷标。

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

查询办法接受Lambda(或二个措施)来选择IQueryable<T>并且再次回到任何对象类型。示举个例子下:

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

因为是采纳拉姆da(或方法)在积存对象的法子中奉行,它会在数据库连接开启之后才被实践。你能够回到实体会集,或贰个实体,或一个具部份字段(注:
非Select *)或其余试行查询后的查询结果集。

(2)新增(insert)

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);

新扩展方法会新扩大实体到数据库并且重返同样的已新扩充实体。InsertAndGetId方法再次来到新添实体的标记符(Id)。当咱们接纳电动递增标记符值且供给获得实体的新发生标记符值时那些好用。InsertOfUpdate会新添或更新实体,采用那壹种是根据Id是不是有值来决定。最后,InsertOrUpdatedAndGetId会在实体被新增添或更新后回来Id值。

具备的法子都具有异步版本可应用在异步开垦模型(见关于异步方法章节)

(3)更新(UPDATE)

IRepository定义3个措施来贯彻立异一个已存在于数据库中的实体。它立异实体并回到一样的实业对象。

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

(4)删除(Delete)

IRepository定了一部分措施来删除已存在数据库中实体。

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个留存的实体,第一个措施接受现有实体的Id。

提起底三个主意接受贰个规则来删除符合条件的实业。要注意,全数符合predicate表达式的实体会先被搜寻而后去除。因而,使用上要十分小心,那是有一点都不小希望导致广大题目,假如果有太多实体符合条件。

持有的办法都兼备async版本来应用在异步开拓模型(见关于异步方法章节)。

(5)其余措施(others)

IRepository也提供一些办法来获得数据表中实体的数码。

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);

负有的法子都具有async版本被选择在异步开拓模型(见关于异步方法章节)。

(六)关于异步方法(About Async methods)

ABP协理异步开垦模型。因而,仓库储存方法具有Async版本。在此间有2个施用异步模型的application
service方法的示范:

public class PersonAppService : AbpWpfDemoAppServiceBase, IPersonAppService
{
    private readonly IRepository<Person> _personRepository;

    public PersonAppService(IRepository<Person> personRepository)
    {
        _personRepository = personRepository;
    }

    public async Task<GetPeopleOutput> GetAllPeople()
    {
        var people = await _personRepository.GetAllListAsync();

        return new GetPeopleOutput
        {
            People = Mapper.Map<List<PersonDto>>(people)
        };
    }
}

GetAllPeople方法是异步的还要选拔GetAllListAsync与await保留重要字。

Async不是在各类OOdysseyM框架都有提供。

上例是从EF所提供的异步才干。假诺O宝马X3M框架未有提供Async的蕴藏方法则它会以协同的办法操作。同样地,比方来讲,InsertAsync操作起来和EF的疯长是均等的,因为EF会直到单元作业(unit
of work)完结之后才会写入新实体到数据库中(DbContext.SaveChanges)。

积攒的兑现

ABP在统一打算上是选用不点名特定OPAJEROM框架或别的存取数据库本领的主意。只要完毕IRepository接口,任何框架都足以采纳。

仓储要选择NHibernate或EF来完结都很轻巧。见完成这个框架在ABP仓库储存对象上一文:

  • NHibernate
  • EntityFramework

当您使用NHibernate或EntityFramework,如若提供的主意已丰盛使用,你就不须要为您的实体创立仓库储存对象了。我们得以从来注入IRepository<TEntity>(或IRepository<TEntity,
TPrimaryKey>)。下边包车型客车演示为application
service使用仓储对象来新增添实体到数据库:

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);
    }
}

PersonAppService的建构子注入了IRepository<Person>并且动用其Insert方法。当你有亟待为实体成立三个客制的储存方法,那么你就应当制造四个积存类给内定的实业。

治本数据库连接

数据库连接的拉开和停业,在存款和储蓄方法中,ABP会自动化的张开延续管理。

当仓库储存方法被调用后,数据库连接会自行开启且运行职业。当仓库储存方法试行实现并且再次回到以往,全部的实体变化都会被贮存,
事务被交付并且数据库连接被关门,一切都由ABP自动化的垄断(monopoly)。假诺存款和储蓄方法抛出任何类型的相当,事务会自动地回滚并且数据连接会被关闭。上述全数操作在贯彻了IRepository接口的囤积类具备公开的措施中都能够被调用。

借使存储方法调用其余仓库储存方法(即就是例外仓储的主意),它们共享同二个总是和作业。连接会由仓库储存方法调用链最上层的不得了仓库储存方法所管理。愈来愈多关于数据库管理,详见UnitOfWork文件。

储的生命周期

具备的累积对象都以暂且的。那就是说,它们是在有要求的时候才会被创建。ABP大批量的应用依赖注入,当仓库储存类要求被注入的时候,新的类实体会由注入容器会活动地创设。见相根据注入文件有越来越多音信。

储存的一级推行

  • 对此3个T类型的实体,是能够应用IRepository<T>。但别任何情形下都创立定制化的囤积,除非大家的确很需求。预约义仓库储存方法已经足足应付各个案例。
  • 倘若你正开创定制的积攒(能够达成IRepository<TEntity>)
    • 积存类应该是无状态的。那代表,
      你不应该定义仓库储存等第的情景对象并且仓储方法的调用也不应有影响到任何调用。    
    • 当仓库储存能够接纳相依照注入,尽可较少或许不相依照于任何服务。 

 


 

瞩望越来越多国内的架构师能关心到ABP那些类型,也许这其中有能帮忙到您的地点,或然有您的到场,这一个类别能够进步得越来越好。

招待加ABP架构划设想计交换QQ群:1347十70七

图片 2

 

点那里进入ABP体系小说总目录