<<ABP框架>> 仓储

文档目录

本节内容:

  • 默认仓储
  • 自定义仓储
    • 自定义仓储接口
    • 自定义仓储实现
  • 基仓储方法
    • 查询

      • 获取单个实体
      • 获取实体列表
    • 关于 IQueryable
      • 自定义返回值
    • 插入
    • 更新
    • 删除
    • 其它
    • 关于异步方法
  • 管理数据库连接
  • 一个仓储的生命周期
  • 仓储最佳实践

领域和映射层之间的媒介使用一种类似集合的接口来访问实体。通常地,每个实体(或聚合根)使用一个分离的仓储。

默认仓储

在ABP里,一个仓储类实现IRepository<TEntity,TPrimaryKey>接口。ABP默认地为每个实体类型自动创建一个默认仓储。你可以直接注入IRepository<TEntity>(或IRepository<TEntity,TPrimaryKey>)。一个应用服务使用仓储把一个实体插入数据库的例子:

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方法。

自定义仓储

只有当实体需要创建一个自定义的仓储方法时,才需要你创建一个仓储类。

自定义仓储接口

如下示例,为一个Person实体定义一个仓储:

public interface IPersonRepository : IRepository<Person>
{

}

IPersonRepository扩展了IRepository<TEntity>,它用来定义具有int(Int32)类型Id属性的实体。如果你的实体键不是int,你可以扩展IRepository<TEntity,TPrimaryKey>接口,如下所示:

public interface IPersonRepository : IRepository<Person, long>
{

}

自定义仓储实现

ABP设计成与ORM(对象/关系映射)框架分离、或其它访问数据库技术分离。仓储开箱即用地实现了NHibernate和EntityFramework。查看ABP对这些框架的实现的相关文档:

  • NHibernate integration
  • EntityFramework integration

基仓储方法

每个仓储包含一些通用的来自IRepository<TEntity>接口的方法,我们在此把它们的大部分方法,研究一下。

查询

获取一个单独实体

TEntity Get(TPrimaryKey id);
Task<TEntity> GetAsync(TPrimaryKey id);
TEntity Single(Expression<Func<TEntity, bool>> predicate);
Task<TEntity> SingleAsync(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)的实体。如果无法从数据库中找到给定Id的实体,将抛出异常。Single方法类似于Get方法,但接受一个表达式,而不是一个Id,所以你可以写一个lambda表达式来获取一个实体,用法示例:

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

注意:Single会在无法获取符合表达式的实体,或是有多个符合表达式的实体时,抛出异常。

FirstOrDefault类似,但在找不到给定Id的实体时,返回null(代替抛出异常)。如果找到多个实体,则返回第一个。

Load不从数据库获取实体,但为延迟加载创建一个代理对象。如果你只是使用Id属性,那么实质上,不会从数据库中获取实体,只有当你访问实体的其它属性时,它才从数据库中获取实体。出于性能考虑,这个方法用来代替Get。它已经在NHibernate中实现了。如果ORM供应器没有实现它,Load方法就跟Get方法是一样的。

获取一个实体列表

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

GetAll返回IQueryable<T>,所以你在这个方法后可以添加Linq方法,例如:

//Example 1
var query = from person in _personRepository.GetAll()
            where person.IsActive
            orderby person.Name
            select person;
var people = query.ToList();

//Example 2:
List<Person> personList2 = _personRepository.GetAll().Where(p => p.Name.Contains("H")).OrderBy(p => p.Name).Skip(40).Take(20).ToList();

通过实例GetAll,几乎可以把写所有查询写在Linq里,甚至它可以用在一个json表达式里。

关于 IQueryable<T>

当你在一个仓储方法之外调用GetAll(),必须有一个打开的数据库连接,这是因为IQueryable<T>是延迟执行的。它不会执行数据库的查询,除非你调用ToList()方法或在一个foreach循环(或其它方式访问查询里的项)。所以,当你调用ToList()方法时,数据库连接必须可用。对于一个Web项目,在部分情况你不必关心这个,因为Mvc控制器方法默认都是工作单元,且数据库连接在整个请求里都是可用的。为更好地理解它,请查看工作单元文档。

自定义返回值

还有一个另外的方法提供更强大的IQueryable,可以用在工作单元之外。

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

Query方法接受一个lambda表达式(或方法),该表达式(或方法)接收IQueryable<T>并返回任何类型的对象。例如:

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

由于给定的lambda(或方法)在仓储方法在执行,当数据库连接可用时,它被执行。你可以在执行查询后,返回实体列表、单个实体、一个投射或其它。

插入

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

Insert方法简单地把一个新实体插入到数据库,并返回此插入的实体。InsertAndGetId方法为新插入的实体返回Id,当Id是自增时非常有用。InsertOrUpdate根据Id值执行插入或更新操作。最后,InsertOrUpdateAndGetId在插入或更新实体后,返回它的Id值。

更新

IRepository定义了更新一个已存在于数据库的实体的方法,它获取一个需要更新的实体,返回相同的实体。

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

大部分时间,你不需要显式地调用Update方法,因为工作单元会在完成时调用Update方法。见工作单元文档获取更多信息。

删除

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

第一个方法接受一个已存在的实体,第二个接受要删除实体的Id。最后一个根据给定条件删除所有符合的实体,注意:所有匹配谓词的实体可能会从数据库中先获取到内存,然后再删除(看仓储如何实现了),所以使用它要小心了,这在有大量符合条件的实体时,可能引起性能问题。

其它

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<Func<TEntity, bool>> predicate);

关于异步方法

ABP支持异常编程模式,所以仓储方法有异步版本。如下例子为一个应用服务方法使用异常模式:

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方法是一个异步方式,并使用关键字await调用GetAllListAsync。

可能不是所有的ORM框架都支持异步。EntityFramework支持。如果不支持,异步方法以同步的方式工作。同样的,例如,在EntityFramework中InsertAsync和Insert工作方式相同,因为EF直到工作单元完成前(也就是DbContext.SaveChanges),代码不写入新的实体。

管理数据库连接

在一个仓储方法里,它不打开或关闭数据库连接,ABP自动管理数据库连接。

当进入一个仓储方法,ABP自动打开一个数据库连接并开始一个事务,当这个方法结束并返回时,所有的变化被保存,事务提交后关闭数据库连接。如果你的仓储方法抛出任何类型的异常,自动回滚事务并关闭数据库连接。这适用于所有实现IRepository接口的类的公开方法。

如果一个仓储方法调用另一个仓储方法(即使是一个不同仓储的方法),它们共享相同的连接和事务,第一个方法管理数据库的连接(打开/关闭)。获取更多的数据库连接管理信息,请查阅工作单元文档。

一个仓储的生命周期

所有仓储实例都是短暂的,它的意思是:它们为每次的使用都进行实例化。查阅依赖注入文档获取更多信息。

仓储最佳实践

  • 为一个T类型的实体,尽可能地使用IRepository<T>。不要创建自定义的仓储,除非确实需要。预定义的仓储方法可满足大部分情况。
  • 如果你正在创建一个自定义仓储(通过扩展IRepository〈TEntity>):
    • 仓储类应该没有状态,也就是说:你不应该定义一个仓储级别状态的对象且一个仓储方法的调用不应该影响另一个调用。
    • 自定义仓储方法不应该包含业务逻辑或应用逻辑。它应该只是执行数据相关或ORM相关的任务。
    • 虽然仓储可以使用依赖注入,但尽可能少或不定义对其它服务的依赖。
时间: 2024-11-03 21:30:01

<<ABP框架>> 仓储的相关文章

ABP框架个人开发实战(1)_环境搭建

前言 之前关注ABP框架有一阵子了,一直没有潜下心来实际研究一下.最近想自己建站,以后有自己的功能开发项目,可以在自己的站点上开发,并一步步的完善,所以找个比较好用的框架迫在眉睫,选来选去,决定用ABP框架.用群里的大大门的话来说,掌握了ABP,基本就可以飞天了~ 先简单介绍下吧(以下部分资料来自群里资料,如有侵权,请告知): ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开

ABP框架用Dapper实现通过SQL访问数据库

为了防止不提供原网址的转载,特在这里加上原文链接:http://www.cnblogs.com/skabyy/p/7517397.html 本篇我们实现数据库的访问.我们将实现两种数据库访问方法来访问一个SQLite数据库--使用NHibernate实现的ORM映射访问和使用Dapper实现的SQL语句访问.然后完成前一篇未完成的CreateTweet和GetTweets接口. 在开始之前,先做一些准备工作,新建Domain层的Module: public class MyTweetDomain

ABP架构学习系列三:手工搭建ABP框架

由于公司的项目才接触到ABP这个框架,当时就觉得高大上,什么IOC.AOP.ddd各种专业词汇让人激情 澎湃,但在使用过程中碰到了许多坑,可能也许是没有去看源码导致的,但工作确实没有那么多时间让人去慢慢研究.很久之前想手动搭建这个框架了,但是各种理由,你懂的.但是要在技术上得到大的提升就得静的下心去研究,学到大神的思想和精髓,运用到实际中去,才能去体验更开阔的天地. 本文以创建博客为思路,一步步构建整个项目,在摸索中进步,也希望能够帮助到有需要的人. 一.基础架构 第一部分主要是搭建好整个项目的

ABP框架 - 我的第一个Web API

上一篇我们已经对ABP是什么,能做什么.有了一个印象.那么接下来我们将动手使用ABP框架快速开发一个API,你将会发现使用ABP框架有多么便利,会实实在在感受到它的魅力. 环境要求 Visual Studio 2017 SQL Server .Net Core SDK 创建应用程序 我们使用ABP模板来创建应用程序,访问http://www.aspnetboilerplate.com/Templates,你将会看到如下页面 参照上图所示的选项选择 输入项目名称,我这里是"AbpTraining&

【ABP框架系列学习】介绍篇(1)

  0.引言 该系列博文主要在[官方文档]及[tkbSimplest]ABP框架理论研究系列博文的基础上进行总结的,或许大家会质问,别人都已经翻译过了,这不是多此一举吗?原因如下: 1.[tkbSimplest]的相关博文由于撰写得比较早的,在参照官方文档学习的过程中,发现部分知识未能及时同步(当前V4.0.2版本),如[EntityHistory].[Multi-Lingual Engities]章节未涉及.[Caching]章节没有Entity Caching等内容. 2.进一步深入学习AB

【ABP框架系列学习】N层架构(3)

原文:[ABP框架系列学习]N层架构(3) 目录 0.引言 1.DDD分层 2.ABP应用构架模型 客户端应用程序(Client Applications) 表现层(Presentation Layer) 分布式服务层(Distributed Service Layer) 应用层(Application Layer) 领域层 基础设施层 3.使用ABP项目模版快速生成应用程序 0.引言 应用程序的分层是一种广泛接受的技术, 可以降低复杂度和提高代码的可重用性.为了实现分层架构,ABP遵循领域驱动

[译]ABP框架v2.0 和 ABP商业版

ABP框架v2.0 和 ABP商业版 ABP框架2.0版已经在本周公布.这篇文章解释了为什么我们发布了一个抢先主版本,和2.0版本中的变化. 除了v2.0版本,我们很高兴地宣布ABP商业版,这是建立在开源ABP框架的之上的一套专业的模块,工具,主题和服务. ABP框架V2.0 为什么2.0,而不是1.2? 本来在V1.1.2发布后计划发布1.2版.然而,有报告称1.x版在Linux上有一些性能和稳定性问题,尤其是当应用程序部署在低配CPU和内存的Linux容器上. 我们深入研究了这一问题,并已查

ABP框架 - 规约

文档目录 本节内容: 简介 示例 创建规约类 在仓储里使用规约 组合规约 讨论 何时使用 何时不用 简介 规约模式是一个特别的软件设计模式,业务逻辑可以使用boolean逻辑重新链接业务逻辑(维基百科). 实践中的大部分情况,它是为实体或其它业务对象,定义可复用的过滤器. 示例 在此小节,我们将看到规约模式的必要性,这节是通用的,与ABP的实现无关. 假设你有一个服务方法用来计算客户的总数,如: public class CustomerManager { public int GetCusto

&lt;&lt;ABP框架&gt;&gt; 功能管理

文档目录 本节内容: 简介 关于 IFeatureValueStore 功能类型 Boolean 功能 Value 功能 定义功能 基本功能属性 其它功能属性 功能层次 检查功能 使用RequiresFeature特性 RequiresFeature特性注意事项 使用 IFeatureChecker IsEnabled GetValue 客户端 isEnabled getValue 功能管理器 对版本的一个提示 简介 大部分SaaS(多租户)应用有不同功能的版本(包),因此你可以提供不同价格和功