基于DDD的.NET开发框架 - ABP仓储实现
ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称。
ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应用程序的新起点,它旨在成为一个通用的WEB应用程序框架和项目模板。
ABP的官方网站:http://www.aspnetboilerplate.com
ABP官方文档:http://www.aspnetboilerplate.com/Pages/Documents
Github上的开源项目:https://github.com/aspnetboilerplate
一、基本操作
仓储的定义:位于领域层和数据映射层之间,使用类似集合的接口来访问领域对象。
在实践中,仓储是执行领域对象(实体和值对象)的数据库操作。一般地,一个分离的仓储用于一个实体(或者聚合根)。
1、IRepository接口
在ABP中,一个仓储类应该实现一个IRepository接口。为每一个仓储定义一个接口是一个好的做法。
一个Person实体的仓储定义如下:
public interface IPersonRepository : IRepository<Person>
{
}
IPersonRepository扩展了IRepository,它用于定义拥有主键类型为int32的实体。如果你的实体不是int,那么可以扩展IRepository<tentity,tprimarykey>接口,如下所示:
public interface IPersonRepository : IRepository<Person, long>
{
}
IRepository为仓储类定义了最通用的方法,如select,insert,update和delete方法(CRUD操作)。大多数情况下,这些方法对于简单的实体是足够了。如果这些方法对于一个实体来说已经足够了,那么就没有必要为这个实体创建仓储接口和仓储类了。
1)、查询:
IRepository定义了通用的方法,从数据库中检索实体。
获取单个实体:
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)的实体。如果在数据库中没有找到这个实体,就会抛出异常。Single方法和Get类似,但是它的参数是一个表达式而不是一个Id。因此,你可以使用Lambda表达式获得一个实体。比如:
var person = _personRepository.Get();
var person = _personRepository.Single(p => p.Name == "hi joye.net");
注意:如果根据给定的条件没有查找出实体或者查出不止一个实体,那么Single方法会抛出异常。
FirstOrDefault是相似的,但是如果根据给的的Id或者表达式没有找到实体,那么就会返回null。如果对于给定的条件存在不止一个实体,那么会返回找到的第一个实体。
Load方法不会从数据库中检索实体,但是会创建一个用于懒加载的代理对象。如果你只用了Id属性,那么Entity实际上并没有检索到。只有你访问实体的其他属性,才会从数据库中检索。考虑到性能因素,这个就可以替换Get方法。这在NHiberbate中也实现了。如果ORM提供者没有实现它,那么Load方法会和Get方法一样地工作。
一些方法有用于async编程模型的异步版本。
获得实体的列表:
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 > );
GetAll返回的类型是IQueryable。因此,你可以在此方法之后添加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().Take().ToList();
有了GetAll方法,几乎所有的查询都可以使用Linq重写。甚至可以用在一个连接表达式中。
关于IQueryable:脱离了仓储方法调用GetAll()方法时,数据库连接必须要打开。这是因为IQueryable的延迟执行。直到调用ToList()方法或者在foreach循环中使用IQueryable(或者访问查询到的元素)时,才会执行数据库查询操作。因此,当调用ToList()方法时。数据库连接必须打开。这可以通过ABP中的UnitOfWork特性标记调用者方法来实现。注意:应用服务方法默认已经是UnitOfWork,因此,即使没有为应用服务层方法添加UnitOfWork特性,GetAll()方法也会正常工作。
这些方法也存在用于异步编程模型的asyn版本。
自定义返回值:
也存在提供了IQueryable的额外方法,在调用的方法中不需要使用UnitOfWork。
T Query<T>(Func<IQueryable<TEntity>, T> queryMethod);
Query方法接受一个接收IQueryable的lambda(或方法),并返回任何对象的类型。用法如下:
var people = _personRepository.Query(q => q.Where(p => p.Name.Contains("H")).OrderBy(p => p.Name).ToList());
在该仓储方法中,因为执行了给定的lambda(或方法),它是在数据库连接打开的时候执行的。你可以返回实体列表,单个实体,一个投影或者执行了该查询的其他东西。
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);
Insert方法简化了将一个实体插入数据库,并将刚刚插入的实体返回。InsertAndGetId方法返回了新插入实体的Id。如果实体的Id是自动增长的并且需要最新插入实体的Id,那么该方法很有用。InsertOrUpdate方法通过检查Id的值插入或更新给定的实体。最后,当插入或者更新之后,InsertOrUpdateAndGetId返回该实体的值。
所有的方法都存在用于异步编程模型的async版本。
3)、更新:
IRepository定义了一个方法来更新数据库中已存在的实体。它可以获得要更新的实体并返回相同的实体对象。
TEntity Update(TEntity entity);
Task<TEntity> UpdateAsync(TEntity entity);
4)、删除:
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。
最后一个方法接受一个删除符合给定条件的所有实体的方法。注意,匹配给定谓词的所有实体都会从数据库中检索到然后被删除。因此,小心使用它,如果给定的条件存在太多的实体,那么可能会造成性能问题。
5)、其他:
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);
6)、关于异步方法:
ABP支持异步编程模型(APM)。因此,仓储方法有异步版本。下面是一个使用了异步模型的应用服务方法例子:
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框架都支持Async,但是EntityFramework支持。如果不支持,异步仓储方法就会同步进行。比如,在EF中,InsertAsync和Insert是等效的,因为直到工作单元完成(Dbcontext.SaveChanges),EF才会将新的实体写入数据库。
2、仓储实现
ABP的设计独立于一个特定的ORM(对象/关系映射)框架或者访问数据库的其他技术。通过实现仓储接口,可以使用任何框架。
ABP使用NHibernate和 EntityFramework实现了开箱即用的仓储。关于这两个ORM框架可以关注后面的文档。
当使用NHibernate或EntityFramework时,如果标准方法是足够使用的话,那么不必为实体类创建仓储了。你可以直接注入IRepository(或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,并使用了Insert方法。这样,当你需要为一个实体创建一个自定义仓储方法时,你才应该为该实体创建一个仓储类。
3、管理数据库的连接:
在仓储方法中,数据库连接是没有打开的或是关闭的。ABP对于数据库连接的管理是自动处理的。
当将要进入一个仓储方法时,数据库连接会自动打开,并且事务自动开始。当仓储方法结束并返回的时候,ABP会自动完成:保存所有的更改,完成事务的提交和关闭数据库连接。如果仓储方法抛出任何类型的异常,那么事务会自动回滚并关闭数据库。这对于所有的实现了IRepository接口的类的公共方法都是成立的。
如果一个仓储方法调用了其他的仓储方法,那么它们会共享相同的连接和事务。进入仓储的第一个方法会管理数据库的连接。更多信息,请留意后面博客的工作单元。
4、仓储的生命周期
所有的仓储实例都是Transient(每次使用时都会实例化)的。ABP强烈推荐使用依赖注入技术。当一个仓储类需要注入时,依赖注入的容器会自动创建该类的新实例。
5、仓储的最佳实践
1、对于一个T类型的实体,使用IRepository仓储接口。除非真的需要,否则不要创建自定义的仓储。预定义的仓储方法对于很多情况足够用了。
2、如果你正在创建一个自定义的仓储(通过扩展IRepository):
1)、仓储类应该是无状态的。这意味着,你不应该定义仓储级别的状态对象,而且一个仓储方法调用不应该影响其他的调用。
2)、自定义仓储方法不应该包含业务逻辑或者应用逻辑,而应该只执行数据相关的或者orm特定的任务。
3)、当仓储使用依赖注入时,给其他服务定义更少的或者不要定义依赖。
基于DDD的.NET开发框架 - ABP仓储实现的更多相关文章
- 线上分享-- 基于DDD的.NET开发框架-ABP介绍
前言 为了能够帮助.Net开发者开拓视野,更好的把最新的技术应用到工作中,我在3月底受邀到如鹏网.net训练营直播间为各位学弟学妹们进行ABP框架的直播分享.同时为了让更多的.NET开发者了解ABP框 ...
- 基于DDD的.NET开发框架 - ABP领域服务
返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...
- 基于DDD的.NET开发框架 - ABP初探
返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...
- 基于DDD的.NET开发框架 - ABP分层设计
返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...
- 基于DDD的.NET开发框架 - ABP工作单元(Unit of Work)
返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...
- 基于DDD的.NET开发框架 - ABP的Entity设计思想
返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...
- 基于DDD的.NET开发框架 - ABP依赖注入
返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...
- 基于DDD的.NET开发框架ABP实例,多租户 (Sass)应用程序,采用.NET MVC, Angularjs, EntityFramework-介绍
介绍 基于ABPZERO的多租户 (Sass)应用程序,采用ASP.NET MVC, Angularjs-介绍 ASP.NET Boilerplate作为应用程序框架. ASP.NET MVC和ASP ...
- 基于DDD的.NET开发框架ABP实例,多租户 (Saas)应用程序,采用.NET MVC, Angularjs, EntityFramework-介绍
介绍 基于ABPZERO的多租户 (Saas)应用程序,采用ASP.NET MVC, Angularjs-介绍 ASP.NET Boilerplate作为应用程序框架. ASP.NET MVC和ASP ...
随机推荐
- PowerBI 引入时间智能
简介 Power BI Desktop -是一款由微软发布的自助式商业智能工具,功能强大.易于使用.其中还可以通过微软云连多个数据源并且使用数据源来创建可视化表盘. 但是几乎所有的BI都需要展示如何随 ...
- 用wget扒站时遇到电信劫持
今天用wget扒下来一个html template的站. 挂在自己机器上后随便点什么,都出电信广告.仔细检查,我勒个去... 扒站过程中,刚好被电信打了劫,看看它给我下载下来的bootstrap.mi ...
- Centos和Redhat的区别和联系
网上看到的,转载给大家 CentOS与RedHat的关系: RedHat在发行的时候,有两种方式:二进制的发行方式以及源代码的发行方式.无论是哪一种发行方式,你都可以免费获得(例如从网上下载),并再次 ...
- simple-spring-memcached缓存搭建
项目中使用的缓存经常是知道使用,没有试过搭建起它.刚好这次自己的毕业可以用来搭建缓存.其他不多说了,直接看操作吧.首先在pom.xml中依赖simple-spring-memcached的架包. &l ...
- 关闭 Sublime Text 3 自动更新
打开Submine Text,找到Preferences -> Settings-User写入 "update_check":false,PS:一定要加逗号.不会可以看图片 ...
- Hibernate延迟加载Lazy
Hibernate延迟加载Lazy 延迟加载(lazy load)又称为懒加载,延迟加载的机制是为了避免一些无谓性能的开销而提出来的,所谓延迟加载就是当在真正需要数据的时候,才真正执行数据加载操作 如 ...
- 常用Meta整理
标签提供关于HTML文档的元数据.元数据不会显示在页面上,但是对于机器是可读的.它可用于浏览器(如何显示内容或重新加载页面),搜索引擎(关键词),或其他web 服务. ——W3School 必要属性 ...
- 在Ubuntu14.04下安装vsftp服务器
猜想在Ubuntu下搭建ftp服务器来实现windows和ubuntu下文件互传是一件很简单的事儿,但是在网上找了好几篇文章都不行,故自己在这里总结一下方法. 首先安装vsftp服务器 sudo ap ...
- python反弹tty shell
linux环境下进行内外渗透需要反弹shell连接内网ssh,Telnet等,就需要tty层的shell了.python代码转过来的,备用. #!/usr/bin/python import sys ...
- http协议(八)请求首部字段
请求首部字段 定义:请求首部字段是从客户端到服务器发送请求报文中所使用的字段,里面包含了附加信息.客户端信息以及对响应内容相关的优先级等内容 1.Accept 通知服务器用户代理可处理的媒体类型及媒体 ...