ABP领域层
1、实体Entites
1.1 概念
实体是DDD(领域驱动设计)的核心概念之一。
实体是具有唯一标识的ID且存储在数据库总。实体通常被映射成数据库中的一个表。
在ABP中,实体继承自Entity类。
public class Person : Entity { public virtual string Name { get; set; } }
Id是所有继承自Entity类的实体主键。
Id数据类型可以被更改,默认是int(int32)类型。
public class Person : Entity<string> { public virtual string Name { get; set; } }
1.2 接口约定
1.2.1 审计(Auditing)
IHasCreationTime具有CreationTime属性,当该实体被插入到数据库时, ABP会自动设置该属性的值为当前时间。
public interface IHasCreationTime { DateTime CreationTime { get; set; } }
ICreationAudited 扩展自IHasCreationTime 并且该接口具有属性CreatorUserId。当保存一个新的实体时,ABP会自动设置CreatorUserId 的属性值为当前用户的Id。
public interface ICreationAudited : IHasCreationTime { long? CreatorUserId { get; set; } }
IModificationAudited具有LastModificationTime 和 LastModifierUserId,当更新一个实体时,APB会自动设置这些属性的值。
public interface IModificationAudited { DateTime? LastModificationTime { get; set; } long? LastModifierUserId { get; set; } }
IAudited 实现所有的审计属性
public interface IAudited : ICreationAudited, IModificationAudited { }
AuditedEntity类已经实现了所有审计功能,直接继承该类即可。
1.2.2软删除(Soft delete)
软删除是一个通用的模式,它标记一个实体已经被删除了,而不是实际从数据库中删除记录。
FullAuditedEntity类已经实现了软删除功能,直接继承该类即可。
1.2.3激活状态/闲置状态(Active/Passive)
有些实体需要被标记为激活状态或者闲置状态。那么你可以为实体采取active/passive 状态的方式来实现。基于这个原因而创建的实体,你可以扩展IPassivable 接口来实现该功能。该接口定义了IsActive 的属性。
如果你首次创建的实体被标记为激活状态,你可以在构造函数设置IsActive 属性值为true。
2、仓储Repositories
2.1概念
在领域层和数据映射层的中介,使用类似集合的接口来存取领域对象。
2.2 IRepository接口
在ABP中,仓储类要实现IRepository接口。最好的方式是针对不同仓储对象定义各自不同的接口。
继承方式:(1)IRepository<TEntity> 定义默认Id类型int32的实体。(2)IRepository<TEntity, TPrimaryKey> 定义指定Id类型的实体。
2.2.1 查询(Query)
2.2.2 新增(Insert)
2.2.3 更新(Update)
2.2.4 删除(Delete)
2.2.5 其他方法(Others)
2.2.6 关于异步方法(About Async methods)
2.3 仓储的实现
ABP在设计上是采取不指定特定ORM框架或其它存取数据库技术的方式。只要实现IRepository 接口,任何框架都可以使用。
仓储要使用NHibernate或EntityFramework来实现都很简单,直接注入IRepository<TEntity>(或IRepository<TEntity, TPrimaryKey>)。
2.4 管理数据库连接
数据库连接的开启和关闭,在仓储方法中,ABP会自动化的进行连接管理。
2.5 仓储的生命周期
所有的仓储对象都是暂时性的。这就是说,它们是在有需要的时候才会被创建。ABP大量的使用依赖注入,当仓储类需要被注入的时候,新的类实体会由注入容器会自动地创建。
3 工作单元Unit of Work
3.1 通用连接和事务管理方法
连接和事务管理是使用数据库的应用程序最重要的概念之一。当你开启一个数据库连接,什么时候开始事务,如何释放连接...诸如此类的。
在应用程序中,有两个通用的方来创建/释放一个数据库连接:
(1)在Web请求到达的时候, 创建一个连接对象。使用同一个连接对象来处理所有的数据库操作,并且在请求结束的时候关闭/释放这个连接。
(2)创建一个连接当需要的时候(只要在使用它之前)并且释放它在使用它之后。这是相当高效的,但是就得乏味而且反复的去进行(创建/释放连接)。
3.2 ABP的连接和事务管理
ABP综合上述两个连接管理的方法,并且提供一个简单而且高效的模型。
3.2.1 仓储类(Repository classes)
public class ContentRepository : NhRepositoryBase<Content>, IContentRepository { public List<Content> GetActiveContents(string searchCondition) { var query = from content in Session.Query<Content>() where content.IsActive && !content.IsDeleted select content; if(string.IsNullorEmpty(searchCondition)) { query = query.Where(content => content.Text.Contains(searchCodition)); } return query.ToList(); } }
3.2.2 应用服务(Application service classes)
一个应用服务的方法也被考虑使用工作单元。
public class PersonAppService : IPersonAppService { private readonly IPersonRepository _personRepository; private readonly IStatisticsRepository _statisticsRepository; public PersonAppService(IPersonRepository personRepository,IStatisticsRepository statisticsRepository) { _personRepository = personRepository; _statisticsRepository = statisticsRepository; } public void CreatePerson(CreatePersonInput input) { var person = new Person { Name = input.Name, EmailAddress = input.EmailAddress }; _personRepository = personRepository; _statisticsRepository = statisticsRepository; } }
这是一个应用服务的方法,所以两个仓储共享同一个连接和事务。
3.2.3 工作单元(Unit of work)
工作单元在后台替仓储和应用服务的方法工作。
有两种直接使用的方式:
(1)使用UnitOfWorkAttribute的方式。
[UnitOfWork] public void CreatePerson(CreatePersonInput input) { var person = new Person { Name = input.Name, EmailAddress = input.EmailAddress }; _personRepository.Insert(person); _statisticsRepository.IncrementPeopleCount(); }
(2)使用IUnitOfWorkManager.Begin(...)方法。
(代码略)
3.3 工作单元
3.3.1 禁用工作单元(Disabling unit of work)
工作单元默认是启动的,如需禁用,则需如下配置属性:[UnitOfWork(IsDisabled = true)]。(慎用)
[UnitOfWork(IsDisabled = true)] public virtual void RemoveFriendship(RemoveFriendInput input) { _friendshipRepository.Delete(input.Id); }
3.3.2 无事务的工作单元(Non-transactional unit of work)
工作单元默认上是具事务性的(这是它的天性)。因此,ABP 启动/提交/回滚一个显性的数据库等级的事务。
在有些特殊案例中,事务可能会导致问题,因为它可能会锁住有些数据列或是数据表于数据库中。在此这些情境下, 或许需要禁用数据库等级的事务。
[UnitOfWork(false)] public GetTasksOutput GetTasks(GetTasksInput input) { var tasks = _taskRepository.GetAllWithPeople(input.AssignedPersonId, input.State); return new GetTasksOutput { Tasks = Mapper.Map<List<TaskDto>>(tasks) }; }
[UnitOfWork(false)]等价于[UnitOfWork(isTransaction:false)],后者更具可读性。
注意:这里有一个非事务性UoW的限制。如果你已经位于事务性UoW区域内,设定isTransactional 为false这个动作会被忽略。
3.3.3 工作单元调用其它工作单元(A unit of work method calls another)
当工作单元区域开始,所有的程序代码都会在同一个线程中执行并共享同一个连接事务,直到工作单元区域终止。
3.3.4 自动化的saving changes (Automatically saving changes)
[UnitOfWork] public void UpdateName(UpdateNameInput input) { var person = _personRepository.Get(input.PersonId); person.Name = input.NewName; }
Name即被修改。
3.3.5 仓储接口的GetAll()方法(IRepository.GetAll() method)
当你在仓储方法外调用GetAll方法, 这必定得有一个开启状态的数据库连接,因为它返回IQueryable 类型的对象。这是需要的,因为IQueryable 延迟执行。它并不会马上执行数据库查询,直到你调用ToList()方法或在foreach 循环中使用IQueryable(或是存取被查询结果集的情况下)。因此,当你调用ToList()方法,数据库连接必需是启用状态。
[UnitOfWork] public SearchPeopleOutput SearchPeople(SearchPeopleInput input) { //取得 IQueryable<Person> var query = _personRepository.GetAll(); //若有选取,则添加一些过滤条件 if (!string.IsNullOfEmpty(input.SearchedName)) { query = query.Where(person =>person.Name.StartsWith(input.SearchedName)); } if (input.IsActive.HasValue) { query = query.Where(person => person.IsActive == input.IsActive.Value); } //取得分页结果集 var people = query.Skip(input.SkipCount).Take(input.MaxResultCount).ToList(); return new SearchPeopleOutput { People = Mapper.Map<List<PersonDto>>(people) }; }
3.3.6 工作单员属性的限制(UnitOfWork attribute restrictions)
使用UnitOfWork属性标签的情况:
(1)类所有public或public virtual这些基于界面的方法(像是应用服务是基于服务界面)。
(2)自我注入类的public virtual方法(像是MVC Controller和Web API Controller) 。
(3)所有protected virtual方法。
3.4 选项
可以在startup configuration 中改变所有工作单元的所有默认值。
public class SimpleTaskSystemCoreModule : AbpModule { public override void PreInitialize() { Configuration.UnitOfWork.IsolationLevel = IsolationLevel.ReadCommitted; Configuration.UnitOfWork.Timeout = TimeSpan.FromMinutes(); } //...其它模块方法 }
3.5 方法
工作单元系统运作是无缝且不可视的。但是,在有些特例下,你需要调用它的方法。
SaveChanges
可以注入IUnitOfWorkManager并且调用IUnitOfWorkManager.Current.SaveChanges()方法。
3.6 事件
IUnitOfWorkManager.Current 属性来取得当前已激活的工作单元并且注册它的事件。你或许会想要执行有些程序代码于当前工作单元成功地完成。示例:
public void CreateTask(CreateTaskInput input) { var task = new Task { Description = input.Description }; if (input.AssignedPersonId.HasValue) { task.AssignedPersonId = input.AssignedPersonId.Value; _unitOfWorkManager.Current.Completed += (sender, args) => { }; } _taskRepository.Insert(task); }
未完待续......
ABP领域层的更多相关文章
- ABP(现代ASP.NET样板开发框架)系列之10、ABP领域层——实体
点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之10.ABP领域层——实体 ABP是“ASP.NET Boilerplate Project (ASP.NET样板 ...
- ABP(现代ASP.NET样板开发框架)系列之11、ABP领域层——仓储(Repositories)
点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之11.ABP领域层——仓储(Repositories) ABP是“ASP.NET Boilerplate Proj ...
- ABP(现代ASP.NET样板开发框架)系列之12、ABP领域层——工作单元(Unit Of work)
点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之12.ABP领域层——工作单元(Unit Of work) ABP是“ASP.NET Boilerplate Pr ...
- ABP(现代ASP.NET样板开发框架)系列之13、ABP领域层——数据过滤器(Data filters)
点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之13.ABP领域层——数据过滤器(Data filters) ABP是“ASP.NET Boilerplate P ...
- ABP(现代ASP.NET样板开发框架)系列之14、ABP领域层——领域事件(Domain events)
点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之14.ABP领域层——领域事件(Domain events) ABP是“ASP.NET Boilerplate P ...
- ABP领域层——领域事件(Domain events)
ABP领域层——领域事件(Domain events) 基于DDD的现代ASP.NET开发框架--ABP系列之14.ABP领域层——领域事件(Domain events) ABP是“ASP.NET B ...
- ABP领域层——工作单元(Unit Of work)
ABP领域层——工作单元(Unit Of work) 点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之12.ABP领域层——工作单元(Unit Of work) ...
- ABP领域层——仓储(Repositories)
ABP领域层——仓储(Repositories) 点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之11.ABP领域层——仓储(Repositories) ABP是 ...
- ABP领域层——实体
ABP领域层——实体 基于DDD的现代ASP.NET开发框架--ABP系列之10.ABP领域层——实体 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的 ...
- ABP领域层知识回顾之---工作单元
1. 前言 在上一篇博文中(http://www.cnblogs.com/xiyin/p/6842958.html) 我们讲到了ABP领域层的仓储.这边博文我们来讲 工作单元.个人觉得比较重要.文 ...
随机推荐
- ABP文档 - SignalR 集成
文档目录 本节内容: 简介 安装 服务端 客户端 连接确立 内置功能 通知 在线客户端 帕斯卡 vs 骆峰式 你的SignalR代码 简介 使用Abp.Web.SignalR nuget包,使基于应用 ...
- HTML BOM Browser对象
BOM:Browser Object Model,即浏览器对象模型,提供了独立于内容的.可以与浏览器窗口进行互动的对象结构. Browser对象:指BOM提供的多个对象,包括:Window.Navig ...
- HTML5 介绍
本篇主要介绍HTML5规范的内容和页面上的架构变动. 目录 1. HTML5介绍 1.1 介绍 1.2 内容 1.3 浏览器支持情况 2. 创建HTML5页面 2.1 <!DOCTYPE> ...
- 了解PHP中的Array数组和foreach
1. 了解数组 PHP 中的数组实际上是一个有序映射.映射是一种把 values 关联到 keys 的类型.详细的解释可参见:PHP.net中的Array数组 . 2.例子:一般的数组 这里,我 ...
- 算法与数据结构(十三) 冒泡排序、插入排序、希尔排序、选择排序(Swift3.0版)
本篇博客中的代码实现依然采用Swift3.0来实现.在前几篇博客连续的介绍了关于查找的相关内容, 大约包括线性数据结构的顺序查找.折半查找.插值查找.Fibonacci查找,还包括数结构的二叉排序树以 ...
- spring remoting源码分析--Hessian分析
1. Caucho 1.1 概况 spring-remoting代码的情况如下: 本节近分析caucho模块. 1.2 分类 其中以hession为例,Hessian远程服务调用过程: Hessian ...
- zookeeper源码分析之三客户端发送请求流程
znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个功能是zookeeper对于应用最重要的特性,通过这个特性可以实现的功能包括配置的 ...
- C#如何在PDF文件添加图片印章
文档中添加印章可以起一定的作用,比如,防止文件随意被使用,或者确保文档内容的安全性和权威性.C#添加图片印章其实也有很多实现方法,这里我使用的是免费的第三方软件Free Spire.PDF,向大家阐述 ...
- 简单分析JavaScript中的面向对象
初学JavaScript的时候有人会认为JavaScript不是一门面向对象的语言,因为JS是没有类的概念的,但是这并不代表JavaScript没有对象的存在,而且JavaScript也提供了其它的方 ...
- 豪情-CSS解构系列之-新浪页面解构-01
目录: 一. 新浪的布局特点 二. 内容细节的特点 三. 其中相关的一些基础技术点 1. 常见布局方法 2. 布局要点 3. Debugger误区 4.列表 5.字体颜色 6.CSS选择符 7.CSS ...