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); }