Connection and transaction management is one of the most important concepts in an application that uses a database. When to open a connection, when to start a transaction, how to dispose the connection and so on.. ASP.NET Boilerplate manages database connection and transactions by it's unit of work system.web
链接和事务管理是使用数据库的应用程序中最重要的概念之一。什么时候打开链接,什么时候启动事务,如何处理链接等等。ASP.NET样板的工做单元管理数据库链接和事务系统。数据库
ASP.NET Boilerplate opens a database connection (it may not be opened immediately, but opened in first database usage, based on ORM provider implementation) and begins a transaction when entering a unit of work method. So, you can use connection safely in this method. At the end of the method, the transaction is commited and the connection is disposed. If the method throws any exception, transaction is rolled back and the connection is disposed. In this way, a unit of work method is atomic (a unit of work). ASP.NET Boilerplate does all of these automatically.安全
ASP.NET样板开放数据库链接(它可能不会当即被打开,可是打开了第一数据库的使用,基于ORM提供程序实现),开始一个事务,当进入一个工做单元的方法。所以,在这种方法中能够安全地使用链接。在方法结束时,事务提交和链接终止。若是该方法抛出任何异常,则回滚事务并设置链接。这样,工做单元的方法就是原子(工做单元)。ASP.NET的样板作全部这些自动。app
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.框架
若是一个工做单元方法调用另外一个工做单元方法,则它们都使用相同的链接和事务。第一种输入方法管理链接和事务,其余使用它。less
Some methods are unit of work methods by default:dom
Assume that we have an application service method like below:async
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.Insert(person); _statisticsRepository.IncrementPeopleCount(); } }
In the CreatePerson method, we're inserting a person using person repository and incrementing total people count using statistics repository. Both of repositories shares same connection and transaction in this example since application service method is unit of work by default. ASP.NET Boilerplate opens a database connection and starts a transaction when entering CreatePerson method and commit the transaction at end of the method if no exception is thrown, rolls back if any exception occurs. In that way, all database operations in CreatePerson method becomes atomic (unit of work). ide
在createperson方法,咱们把 person repository和增长总的人使用统计库统计。在本例中,两个存储库共享相同的链接和事务,由于默认状况下应用程序服务方法是工做单元。ASP.NET样板打开一个数据库链接和启动一个事务进入createperson方法和提交事务结束的方法若是没有异常被抛出,回滚若是发生任何异常。这样,在CreatePerson,全部的数据库操做方法成为原子(工做单位)。函数
In addition to default conventional unit of work classes, you can add your own convention in PreInitialize method of your module like below:
除了默认的工做常规单元类,你能够添加你本身的惯例在分发你模块像下面的方法:
Configuration.UnitOfWork.ConventionalUowSelectors.Add(type => ...);
You should check type and return true if this type should be a conventional unit of work class.
Unit of work implicitly works for the methods defined above. In most cases you don't have to control unit of work manually for web applications. You can explicitly use it if you want to control unit of work somewhere else. There are two approaches for it.
工做单元隐式地工做于上面定义的方法。在大多数状况下,您没必要手动控制Web应用程序的工做单元。若是想在其余地方控制工做单元,能够显式地使用它。它有两种方法。
First and preferred approach is using UnitOfWork attribute. Example:
[UnitOfWork] public void CreatePerson(CreatePersonInput input) { var person = new Person { Name = input.Name, EmailAddress = input.EmailAddress }; _personRepository.Insert(person); _statisticsRepository.IncrementPeopleCount(); }
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. Also see 'unit of work method restrictions' section.
所以,createperson方法成为工做单位和管理数据库链接和事务,都使用相同的单元库的工做。注意,若是这是一个应用服务的方法不须要UnitOfWork属性。还请参见“工做单元方法限制”部分。
There are some options of the UnitOfWork attribute. See 'unit of work in detail' section. UnitOfWork attribute can also be used for classes to configure all methods of a class. Method attribute overrides the class attribute if exists.
有一些选项UnitOfWork属性。参见“详细工做单元”部分。UnitOfWork属性也可用于类配置一个类的全部方法。若是存在,方法属性重写类属性。
Second appropaches is using the IUnitOfWorkManager.Begin(...) method as shown below:
public class MyService { private readonly IUnitOfWorkManager _unitOfWorkManager; private readonly IPersonRepository _personRepository; private readonly IStatisticsRepository _statisticsRepository; public MyService(IUnitOfWorkManager unitOfWorkManager, IPersonRepository personRepository, IStatisticsRepository statisticsRepository) { _unitOfWorkManager = unitOfWorkManager; _personRepository = personRepository; _statisticsRepository = statisticsRepository; } public void CreatePerson(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 (Some base classes have already UnitOfWorkManager injected by default: MVC Controllers, application services, domain services...). Thus, you can create morelimited 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.
你能够注入和使用iunitofworkmanager以下所示(一些基类已经unitofworkmanager注入默认:MVC控制器,应用服务、域名服务…)。所以,您能够建立有限范围的装置做品。在这种方法中,您应该手动调用完整方法。若是不调用,事务将回滚,而更改不会保存。
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.
开始方法有重载来设置工做单元选项。最好使用短UnitOfWork属性,若是你没有一个好的理由。
You may want to disable unit of work for conventional unit of work methods . To do that, use UnitOfWorkAttribute's IsDisabled property. Example usage:
[UnitOfWork(IsDisabled = true)] public virtual void RemoveFriendship(RemoveFriendshipInput input) { _friendshipRepository.Delete(input.Id); }
Normally, you don't want to do that, but in some situations you may want to disable unit of work:
Note that if a unit of work method calls this RemoveFriendship method, disabling this method is ignored and it uses the same unit of work with the caller method. So, use disabling by carefully. Also, the code above works well since repository methods are unit of work by default.
注意,若是一个单位的工做方法调用此removefriendship法,这种方法是忽略,它使用相同的工做单元与来电者的方法禁用。因此,当心地禁用。此外,上面的代码很好地工做,由于默认状况下存储库方法是工做单元。
A unit of work is transactional as default (by it's nature). Thus, ASP.NET Boilerplate starts/commits/rollbacks an explicit database-level transaction. In some special cases, transaction may cause problems since it may lock some rows or tables in the database. In this situations, you may want to disable database-level transaction. UnitOfWork attribute can get a boolean value in it's constructor to work as non-transactional. Example usage:
工做单元是事务性的缺省(根据其性质)。所以,ASP.NET开始/提交/回滚文件显式数据库事务。在某些特殊状况下,事务可能致使问题,由于它可能会锁定数据库中的某些行或表。在这种状况下,您可能但愿禁用数据库级事务。UnitOfWork属性能够做为非事务性的构造函数的布尔值。使用示例:
[UnitOfWork(isTransactional: false)]
public GetTasksOutput GetTasks(GetTasksInput input)
{
var tasks = _taskRepository.GetAllWithPeople(input.AssignedPersonId, input.State);
return new GetTasksOutput
{
Tasks = Mapper.Map<List<TaskDto>>(tasks)
};
}
I suggest to use this attribute as [UnitOfWork(isTransactional: false)]. I think it's more readable and explicit. But you can use as [UnitOfWork(false)].
我建议使用此属性为[ UnitOfWork(istransactional:false)]。我认为它更易读,更明确。可是,你可使用[ UnitOfWork(false)]。
Note that ORM frameworks (like NHibernate and EntityFramework) internally saves changes in a single command. Assume that you updated a few Entities in a non-transactional UOW. Even in this situation all updates are performed at end of the unit of work with a single database command. But if you execute an SQL query directly, it's performed immediately and not rolled back if your UOW is not transactional.
注意,ORM框架(如NHibernate和EntityFramework)内保存在一个单一的命令更改。假设你在一个非事务性更新UOW几个实体。即便在这种状况下,全部更新都是在工做单元的末端用一个数据库命令执行的。但若是你执行一个SQL查询,它当即执行,若是你不回滚UOW不是事务性的。
There is a restriction for non-transactional UOWs. If you're already in a transactional unit of work scope, setting isTransactional to false is ignored (use Transaction Scope Option to create a non-transactional unit of work in a transactional unit of work).
在非事务性UOWS中有限制。若是你已经在一个事务工做单元的范围,设置istransactional = false 被忽略(使用事务范围选项建立工做非事务性单元在一个事务工做单元)。
Use non-transactional unit of works carefully since most of the times it should be transactional for data integrity. If your method just reads data, not changes it, it can be safely non-transactional.
仔细地使用非事务性的工做单元,由于大多数时候它应该是数据完整性的事务处理。若是您的方法只读取数据,而不更改数据,则能够安全地非事务性地处理。
Unit of work is ambient. If a unit of work method calls another unit of work method, they share same connection and transaction. First method manages connection, others use it.
工做单元是环境。若是一个工做单元方法调用另外一个工做单元方法,它们共享相同的链接和事务。第一种方法管理链接,另外一些方法使用它。
You can create a different and isolated transaction in another transaction or can create a non-transactional scope in a transaction. .NET defines TransactionScopeOption for that. You can set Scope option of the unit of work to control it.
您能够在另外一个事务中建立不一样的、独立的事务,也能够在事务中建立非事务做用域。transactionscopeoption。net定义为。您能够设置工做单元的范围选项来控制它。
If a method is unit of work, ASP.NET Boilerplate saves all changes at the end of the method automatically. Assume that we need method to update name of a person:
[UnitOfWork] public void UpdateName(UpdateNameInput input) { var person = _personRepository.Get(input.PersonId); person.Name = input.NewName; }
That's all, name was changed! We did not even called _personRepository.Update method. O/RM framework keep track of all changes of entities in a unit of work and reflects changes to the database.
就这样,名字被改变了!咱们甚至没有调用_personrepository.update方法。O/RM框架跟踪工做单元中全部实体的变化,并反映对数据库的更改。
Note that no need to declare UnitOfWork for conventional unit of work methods.
注意,不须要申明的工做方法常规单元UnitOfWork。
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.
当你调用getall()仓库的方法,必须有一个开放的数据库链接,由于它返回IQueryable。这是由于延期执行IQueryable。它不除非你调用tolist()方法或在foreach循环使用IQueryable执行数据库查询(或者访问查询项目)。因此,当你调用tolist()方法、数据库链接必须活着。
Consider the example below:
[UnitOfWork] public SearchPeopleOutput SearchPeople(SearchPeopleInput input) { //Get IQueryable<Person> var query = _personRepository.GetAll(); //Add some filters if selected if (!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 list var people = query.Skip(input.SkipCount).Take(input.MaxResultCount).ToList(); return new SearchPeopleOutput { People = Mapper.Map<List<PersonDto>>(people) }; }
Here, SearchPeople method must be unit of work since ToList() method of IQueryable is called in the method body, and database connection must be open when IQueryable.ToList() is executed.
在这里,searchpeople方法必须工做单位自IQueryable tolist()方法的方法体中,与数据库的链接必须打开时执行tolist() IQueryable。
In most cases you will use GetAll method safely in a web application, since all controller actions are unit of work by default and thus database connection is available in entire request.
在大多数状况下,你会在一个Web应用程序安全的使用GetAll,由于全部的控制器操做的默认工做单元,所以能够在整个请求数据库链接。
You can use UnitOfWork attribute for;
用于经过接口使用的类的全部公共或公共虚拟方法(如在服务接口上使用的应用程序服务)。
用于自注入类的全部公共虚拟方法(如MVC控制器和Web API控制器)。
全部受保护的虚拟方法。
It's suggested to always make the method virtual. You can not use it for private methods. Because, 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.
建议始终将方法虚拟化。不能用于私有方法。由于,ASP.NET样板使用动态代理,私有方法不能被派生类视。UnitOfWork属性(和任何代理)不工做若是你不使用依赖注入和实例化类本身的。
There are some options can be used to change behaviour of a unit of work.
First, we can change default values of all unit of works in the startup configuration. This is generally done in PreInitialize method of our module.
首先,咱们能够在启动配置中更改全部工做单元的默认值。这通常是在咱们的模块的方法进行操做。
public class SimpleTaskSystemCoreModule : AbpModule { public override void PreInitialize() { Configuration.UnitOfWork.IsolationLevel = IsolationLevel.ReadCommitted; Configuration.UnitOfWork.Timeout = TimeSpan.FromMinutes(30); } //...other module methods }
Second, we can override defaults for a particular unit of work. For that, UnitOfWork attribute constructor and IUnitOfWorkManager.Begin method have overloads to get options.
其次,咱们能够重写特定工做单元的缺省值。为此,UnitOfWork属性构造函数和方法重载iunitofworkmanager。开始得到方法。
And lastly, you can use startup configuration to configure default unit of work attributes for ASP.NET MVC, Web API and ASP.NET Core MVC Controllers (see their documentation).
最后,你可使用启动配置配置ASP.NET MVC的工做属性的默认单位,Web API和ASP.NET核心的MVC控制器(见文献)。
UnitOfWork system works seamlessly and invisibly. But, in some special cases, you need to call it's methods.
系统能够无缝的和无形UnitOfWork。可是,在某些特殊状况下,您须要调用它的方法。
You can access to current unit of work in one of two ways:
您能够经过如下两种方式之一访问当前工做单元:
ASP.NET Boilerplate saves all changes at end of a unit of work, you don't have to do anything. But, sometimes, you may want to save changes to database in middle of a unit of work operation. An example usage may be saving changes to get Id of a new inserted Entity in EntityFramework.
You can use SaveChanges or SaveChangesAsync method of current unit of work.
Note that: if current unit of work is transactional, all changes in the transaction are rolled back if an exception occurs, even saved changes.
ASP.NET样板保存最后一个工做单元全部的改变,你不须要作任何事。可是,有时您可能但愿在工做单元的中间操做中保存对数据库的更改。一个例子使用可保存更改到新插入的ID在EntityFramework的实体。
你可使用调用SaveChanges或如今的工做单位savechangesasync方法。
注意:若是当前工做单元是事务性的,那么若是异常发生,事务中的全部更改都会回滚,甚至保存更改。
A unit of work has Completed, Failed and Disposed events. You can register to these events and perform needed operations. For example,yYou may want to run some code when current unit of work successfully completed. Example:
工做单元完成、失败和处理事件。您能够注册这些事件并执行所需的操做。例如,你可能想运行一些代码,当前的单位工做顺利完成。例子:
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) => { /* TODO: Send email to assigned person */ }; } _taskRepository.Insert(task); }