工作单元位于领域层。
 
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. CDN的实现原理

    在描述CDN的实现原理,让我们先看传统的未加缓存服务的访问过程,以便了解CDN缓存访问方式与未加缓存访问方式的差别: 用户提交域名→浏览器对域名进行解释→得到目的主机的IP地址→根据IP地址访问发出请 ...

  2. SQL 养成一个好习惯是一笔财富

    来源:MR_ke 链接:http://www.cnblogs.com/MR_ke/archive/2011/05/29/2062085.html 我们做软件开发的,大部分人都离不开跟数据库打交道,特别 ...

  3. oracle start with connect by prior 递归查询

    Oracle中的select语句可以用start with...connect by prior子句实现递归查询,connect by 是结构化查询中用到的, 其基本语法是: select ... f ...

  4. JAVA设计模式之模版方法模式

    在阎宏博士的<JAVA与模式>一书中开头是这样描述模板方法(Template Method)模式的: 模板方法模式是类的行为模式.准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式 ...

  5. MFC之动态创建按钮

    打开VS 创建MFC基于对话框的工程,在对话框初始化方法中动态创建一个按钮实例: 1> CButton *pMyButton = new CButton();CEdit *pMyEdit = n ...

  6. 使用jquery获取url以及jquery获取url参数的方法

    使用jquery获取url以及使用jquery获取url参数是我们经常要用到的操作 1.jquery获取url很简单,代码如下 1.window.location.href; 其实只是用到了javas ...

  7. java 中小数点的处理

    第一种 BigDecimal bg = new BigDecimal(f); double f1 = bg.setScale(2, BigDecimal.ROUND_HALF_UP).doubleVa ...

  8. 点击弹出固定大小的新窗口(js实现)

    <SCRIPT LANGUAGE="javascript"> <!-- window.open ('page.html') --> </SCRIPT& ...

  9. MyBatis关联查询,表字段相同,resultMap映射问题的解决办法

    问题描述:在使用mybatis进行多表联合查询时,如果两张表中的字段名称形同,会出现无法正常映射的问题. 问题解决办法:在查询时,给重复的字段 起别名,然后在resultMap中使用别名进行映射. 给 ...

  10. form表单中的常用控件

    <form action="#" method="post"> placeholder:输入表单是提供一个提示 <input type=&qu ...