工作单元位于领域层。
 
ABP的数据库连接和事务处理:
1,仓储类
ASP.NET Boilerplate opens a database connection and begins a transaction when entering a repository method.
当一个仓储方法开始执行时,ABP打开一个数据库连接并启用一个事务。
当仓储方法结束时,事务会被自动提交,数据库连接也会自动释放。
If the repository method throws any exception, transaction is rolled back and the connection is disposed. 
一旦仓储方法抛出任何异常,事务就会回滚,连接也会被释放。
In this way, a repository method is atomic (a unit of work). 

一个仓储方法就是一个工作单元。

If a repository method calls another repository method (in general, if a unit of work method calls another unit of work method), both uses same connection & transaction. The first entering method manages connection & transaction, others use it.
假如仓储方法调用另一个仓储方法,它们使用的是同一个连接,同一个事务。第一个被调用到的仓储方法负责管理连接和事务,另一个仓储方法则单使用不管理。
 
2,应用服务类
publicclass PersonAppService : IPersonAppService
{
privatereadonly IPersonRepository _personRepository;
privatereadonly IStatisticsRepository _statisticsRepository; publicPersonAppService(IPersonRepository personRepository, IStatisticsRepository statisticsRepository)
{
_personRepository = personRepository;
_statisticsRepository = statisticsRepository;
} publicvoidCreatePerson(CreatePersonInput input)
{
var person = new Person { Name = input.Name, EmailAddress = input.EmailAddress };
_personRepository.Insert(person);
_statisticsRepository.IncrementPeopleCount();
}
}

在应用层里的service类中,一个服务方法就是一个工作单元。

原理跟仓储方法是一模一样的。
 
3,工作单元(直接操作Unit of work)
一个做法是使用特性:
[UnitOfWork]
publicvoidCreatePerson(CreatePersonInput input)
{
var person = new Person { Name = input.Name, EmailAddress = input.EmailAddress };
_personRepository.Insert(person);
_statisticsRepository.IncrementPeopleCount();
}

上面的CreatePerson方法使用了[UnitOfWork]特性标签。

Thus, CreatePerson methods becomes unit of work and manages database connection and transaction, both repositories use same unit of work. Note that no need to UnitOfWork attribute if this is an application service method. See 'unit of work method restrictions' section.
CreatePerson方法称为工作单元,它负责管理数据库连接和事务,_personRepository和_statisticsRepository共用同一个工作单元。
要注意的是,如果这是一个应用服务方法,则不需要加UnitOfWork特性标签了。后文将会提及UnitOfWork特性应该用在什么地方。
 
另一个做法是使用 IUnitOfWorkManager.Begin(...)方法:
publicclass MyService
{
privatereadonly IUnitOfWorkManager _unitOfWorkManager;
privatereadonly IPersonRepository _personRepository;
privatereadonly IStatisticsRepository _statisticsRepository; publicMyService(IUnitOfWorkManager unitOfWorkManager, IPersonRepository personRepository, IStatisticsRepository statisticsRepository)
{
_unitOfWorkManager = unitOfWorkManager;
_personRepository = personRepository;
_statisticsRepository = statisticsRepository;
} publicvoidCreatePerson(CreatePersonInput input)
{
var person = new Person { Name = input.Name, EmailAddress = input.EmailAddress }; using (var unitOfWork = _unitOfWorkManager.Begin())
{
_personRepository.Insert(person);
_statisticsRepository.IncrementPeopleCount(); unitOfWork.Complete();
}
}
}

You can inject and use IUnitOfWorkManager as shown here. Thus, you can create more limited scope unit of works. In this approach, you should call Complete method manually. If you don't call, transaction is rolled back and changes are not saved. Begin method has overloads to set unit of work options.

It's better and shorter to use UnitOfWork attribute if you don't have a good reason.

在上面代码的Myservice构造函数中IUnitOfWorkManager会被注入。使用using关键字,可以在一个限定的代码域内使用工作单元。在代码域的最后必须以Complete方法结束。如果不调用这个方法,事务就会回滚。

如无很好的理由,最好还是使用UnitOfWork特性标签,而不是使用_unitOfWorkManager.Begin()
 
更多工作单元细节
 
禁用工作单元
[UnitOfWork(IsDisabled = true)]
publicvirtualvoidRemoveFriendship(RemoveFriendshipInput input)
{
_friendshipRepository.Delete(input.Id);
}

无事务的工作单元

[UnitOfWork(false)]
public GetTasksOutput GetTasks(GetTasksInput input)
{
var tasks = _taskRepository.GetAllWithPeople(input.AssignedPersonId, input.State);
returnnew GetTasksOutput
{
Tasks = Mapper.Map<List<TaskDto>>(tasks)
};
}

这是特殊情况下才使用的东西,可能是因为高并发,希望不要因为使用事务锁住了某些数据表和数据列。

 
一个工作单元调用另一个工作单元
If a unit of work method (a method declared with UnitOfWork attribute) calls another unit of work method, they share same connection and transaction. First method manages connection, others use it. This true for methods run in same Thread (or in same request for web applications). Actually, when a unit of work scope begins, all codes executing in same thread shares same connection and transaction until the unit of work scope ends. This is true both for UnitOfWork attribute and UnitOfWorkScope class. If you create a seperated Thread/Task, it uses own unit of work.
同上面仓储方法里的描述,会共用同一个连接和事务。但是如果两个工作单元方法使用不同的线程,则不会共用。
 
自动保存
[UnitOfWork]
publicvoidUpdateName(UpdateNameInput input)
{
var person = _personRepository.Get(input.PersonId);
person.Name = input.NewName;
}
O/RM framework keep track of all changes of entities in a unit of work and reflects changes to the database.

如上面代码,我们不需要在代码里写_personRepository.Update方法,因为ORM框架会自动追踪变化并反映到数据库中。

 
IRepository.GetAll() 方法
[UnitOfWork]
public SearchPeopleOutput SearchPeople(SearchPeopleInput input)
{
//Get IQueryable<Person>var query = _personRepository.GetAll(); //Add some filters if selectedif (!string.IsNullOrEmpty(input.SearchedName))
{
query = query.Where(person => person.Name.StartsWith(input.SearchedName));
} if (input.IsActive.HasValue)
{
query = query.Where(person => person.IsActive == input.IsActive.Value);
} //Get paged result listvar people = query.Skip(input.SkipCount).Take(input.MaxResultCount).ToList(); returnnew SearchPeopleOutput { People = Mapper.Map<List<PersonDto>>(people) };
}
When you call GetAll() out of a repository method, there must be an open database connection since it returns IQueryable. This is needed because of deferred execution of IQueryable. It does not perform database query unless you call ToList() method or use the IQueryable in a foreach loop (or somehow access to queried items). So, when you call ToList() method, database connection must be alive.

在仓储方法、应用服务方法外面,如果使用到IRepository.GetAll()之类涉及到数据库读写的方法,必须加上UnitOfWork特性标签。

 
工作单元特性标签的限制
  • All public or public virtual methods for classes those are used over interface (Like an application service used over service interface).
  • All public virtual methods for self injected classes (Like MVC Controllers and Web API Controllers).
  • All protected virtual methods.
UnitOfWork特性标签可以用在上面列举的方法上。
建议把方法都写成virtual的。
UnitOfWork特性不能用于private的方法,因为
ASP.NET Boilerplate uses dynamic proxying for that and private methods can not be seen by derived classes. UnitOfWork attribute (and any proxying) does not work if you don't use dependency injection and instantiate the class yourself.
 
选项
publicclass SimpleTaskSystemCoreModule : AbpModule
{
publicoverridevoidPreInitialize()
{
Configuration.UnitOfWork.IsolationLevel = IsolationLevel.ReadCommitted;
Configuration.UnitOfWork.Timeout = TimeSpan.FromMinutes(30);
} //...other module methods
}

SaveChanges

sometimes, you may want to save changes to database in middle of a unit of work operation. In this case, you can inject IUnitOfWorkManager and call IUnitOfWorkManager.Current.SaveChanges() method. An example usage may be saving changes to get Id of a new inserted Entity in EntityFramework. Note that: if current unit of work is transactional, all changes in the transaction are rolled back if an exception occurs, even saved changes.
 
有时候我们想在工作单元还没结束时就保存变化,那么这时就要使用IUnitOfWorkManager.Current.SaveChanges() 方法。
但是要注意的是,即便执行了SaveChanges,最后若有异常抛出,事务发生回滚,SaveChanges也会回滚。
 
事件
三种事件:Completed,Failed ,Disposed
publicvoidCreateTask(CreateTaskInput input)
{
var task = new Task { Description = input.Description }; if (input.AssignedPersonId.HasValue)
{
task.AssignedPersonId = input.AssignedPersonId.Value; _unitOfWorkManager.Current.Completed += (sender, args) => { /* TODO: Send email to assigned person */ };
} _taskRepository.Insert(task);
}
 
 

ABP的工作单元的更多相关文章

  1. ABP框架 - 工作单元

    文档目录 本节内容: 简介 在ABP中管理连接和事务 约定的工作单元 UnitOfWork 特性 IUnitOfWorkManager 工作单元详情 禁用工作单元 非事务性工作单元 工作单元方法调用另 ...

  2. Abp之工作单元与事务

    环境:Abp1.2 疑问:没有调用工作单元的SaveChanges方法引起的事务提交时机的问题. 例如:有一个应用服务代码如下: public void CreatePhrase(PhraseCrea ...

  3. 【ABP】工作单元——不进行事物独立执行功能

    1.注入 private readonly IUnitOfWorkManager unitOfWorkManager; 2.构造 3.开启新事物 using (var unitOfWork = uni ...

  4. ABP官方文档翻译 3.6 工作单元

    工作单元 介绍 ABP中的连接和事务管理 传统的工作单元方法 控制工作单元 UnitOfWork特性 IUnitOfWorkManager 工作单元详情 禁用工作单元 无事务工作单元 一个工作单元方法 ...

  5. EntityFrameworkCore之工作单元的封装

    1. 简介 2. DbContext 生命周期和使用规范 2.1. 生命周期 2.2. 使用规范 2.3. 避免 DbContext 线程处理问题 3. 封装-工作单元 3.1. 分析 3.2. 设计 ...

  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理论学习之工作单元(Unit of Work)

    返回总目录 本篇目录 公共连接和事务管理方法 ABP中的连接和事务管理 仓储类 应用服务 工作单元 工作单元详解 关闭工作单元 非事务的工作单元 工作单元方法调用其它 工作单元作用域 自动保存 IRe ...

  8. 解析ABP框架中的事务处理和工作单元,ABP事务处理

    通用连接和事务管理方法连接和事务管理是使用数据库的应用程序最重要的概念之一.当你开启一个数据库连接,什么时候开始事务,如何释放连接...诸如此类的. 正如大家都知道的,.Net使用连接池(connec ...

  9. 基于DDD的.NET开发框架 - ABP工作单元(Unit of Work)

    返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...

随机推荐

  1. Perl删除数组中元素的多种方法

    Perl中的数组元素起始引用序号为0,@array的第一个元素为$array[0],依次递增,最后一个元素为$array[-1]或者$#array.如果要删除一个数组中已有的元素,可以用以下几个函数来 ...

  2. C#线程并发执行的实例[转]

    实现思路:线程执行后进行阻塞,判断当前标记是否达到设置的并发数,如果未达到上限,执行队列中将继续增加线程:如已达到其余线程排队等候.实例代码: 注:其中用到Mutex与Interlocked两个与线程 ...

  3. FileUploadInterceptor拦截器的笔记

    当请求表单中包含一个文件file,FileUploadInterception拦截器会自动应用于这个文件. 表单: <s:form namespace="/xxx" acti ...

  4. 关于查询扩展版ESI高被引论文的说明

    https://yunpan.cn/ckk5RFV5Emvee 访问密码 e3f7

  5. for语句

    一.for语句的格式格式1:for (控制变量初始化表达式;条件表达式;增量表达式)  语句1; 格式2:for (控制变量初始化表达式;条件表达式;增量表达式){ 语句1;  语句2;  -} 注意 ...

  6. JNI常见错误整理

    ndk开发常见错误1. android.mk文件不存在 $ ndk-buildAndroid NDK: Your APP_BUILD_SCRIPT points to an unknown file: ...

  7. 用自己的ID在appstore中更新app-黑苹果之路

    由于之前套用了别人的镜像,在appstore中更新XCode时总要输别人id的密码,id还不能改.网上有的说要把XCode删掉,然后再用自己的ID更新,找到另外一个方法,更简单: 1.打开引用程序目录 ...

  8. 移动端UC浏览器和QQ浏览器的部分私有meta属性

    UC浏览器 1.设置屏幕横屏还是竖屏 <meta name="screen-orientation" content="portrait | landscape&q ...

  9. 自己开发基于c#的垂直滚动条控件

    由于Visual Studio工具箱中自带滚动条控件外观太老,而且没有颜色外观属性可设置. 所以自己就试着开发一个垂直的滚动条,它可以用来控制TextBox的滚动. 由于代码比较多,源文件已经打包到网 ...

  10. android自定义圆形图片和遇到的问题

    画圆遇到的问题:图片单位不一样,导致图片只能显示出圆的一部分:看代码: public class MyCircleIamge extends ImageView { private Context c ...