Parse the transaction processing and work unit in the ABP framework, and process the abp transaction

Source: Internet
Author: User
Tags connection pooling

Parse the transaction processing and work unit in the ABP framework, and process the abp transaction

General connection and transaction management methods
Connection and transaction management are one of the most important concepts of database applications. When you start a database connection, when to start a transaction, how to release the connection... and so on.

As we all know,. Net uses the connection pool (connection pooling ). Therefore, creating a connection actually retrieves a connection from the connection pool. This will be done because creating a new connection will incur costs. If no connection exists in the connection pool, a new connection object is created and added to the connection pool. When you release the connection, it actually sends the connection object back to the connection pool. This is not an actual release. This mechanism is provided by. Net. Therefore, we should release the connection object after use. This is the best practice.

In an application, there are two common users to create/release a database connection:

Method 1: create a connection object when the Web request arrives. (Application_BeginRequest is an event located in global. asax). It uses the same connection object to process all database operations and closes/releases the connection at the end of the request (Application_EndRequest event ).

This is a simple but inefficient method, because:

  • Maybe this Web request does not need to operate the database, but the connection is enabled. This is an inefficient way to use the connection pool.
  • This may extend the running time of Web requests and require some database operations. This is also an inefficient connection pool.
  • This is feasible for Web applications. If your application is a Widnows Service, this may not be implemented.
  • This is also the best scenario for using transactional database operations. If one operation fails, all operations will be rolled back. Because the transaction locks some data columns (event data tables) in the database, it must be short-lived.

Method 2: create a connection as needed (only before using it) and release it after using it. This is quite efficient, but it is boring and repetitive (creating/releasing connections ).

Connection and transaction management of ABP
In combination with the above two connection management methods, and provides a simple and efficient model.

1. Repository classes)

Warehousing is the main type of database operations. When a database connection is enabled and a transaction is enabled when the data enters the warehouse method. Therefore, you can Securely connect to the storage method. After the warehousing method ends, the transaction is committed and the connection is released. If the warehousing method throws any exception, the transaction will be rolled back and the connection will be released. In this mode, the warehousing method is a unit of work ). The relevant operations are fully automated. Here, there is a simple warehouse:

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(searchCondition));    }    return query.ToList();  }}
 

In this example, nhib.pdf is used as the ORM framework. As shown above, you do not need to write any program code for database connection operations (sessions in nhib.pdf.

If the warehousing method calls another warehousing method (generally, if the unit method calls another unit method), the same connection and transaction are used. The first called warehouse method is used to manage connections and transactions, while the other called warehouse methods are only used and not managed.

2. Application service classes)

Work units are also considered for an application service method. If we have an application service, the method is as follows:

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 add a person who uses the person warehouse and uses the statistics warehouse to increase the total number of people. Two storages share the same connection and transaction in this example, because this is an application service method. If you start a database connection and start a transaction to enter the CreationPerson method, if no exception is thrown, and then commit the transaction at the end of the method, if an exception is thrown, the transaction will be rolled back. Under this mechanism, all database operations in CreatePerson become unit (Work Unit ).

3. Unit of work)

The work unit works in the background for warehousing and application services. If you want to control database connections and transactions, You Need To directly operate the work unit. There are two examples for direct use:

The first and best way to use UnitOfWorkAttribute is as follows:

[UnitOfWork]public void CreatePerson(CreatePersonInput input){  var person = new Person { Name = input.Name, EmailAddress = input.EmailAddress };  _personRepository.Insert(person);  _statisticsRepository.IncrementPeopleCount();}

As a result, the CreatePerson method is transformed into a work unit and manages database connections and transactions. Both storage objects use the same work unit. Note that if this is an application service method, you do not need to add the UnitOfWork attribute. For details, see unit of work method: Chapter 3, 3.3.5.

The second example is to use IUnitOfWorkManager. Begin (...) as follows:

 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 above. Therefore, you can create more limited scope units. In this mechanism, you can call the Complete method manually. If you do not call the API, the transaction will be rolled back and all exceptions will not be stored. The Begin method is overwritten to set the unit of work options.

This is great, but unless you have a good reason, you still need to use the UnitOfWork attribute less.

Work Unit
1. Disabling unit of work)

You may want to disable the work unit of the application service method (because it is enabled by default ). To do this, use the IsDisabled attribute of UnitOfWorkAttribute. Example:

[UnitOfWork(IsDisabled = true)]public virtual void RemoveFriendship(RemoveFriendshipInput input){  _friendshipRepository.Delete(input.Id);}

In general, you do not need to do this because the methods of application services should be unit-based and generally use databases. In some cases, you may want to disable the unit of work of the Application Service:

(1) Your method does not require any database operations and you do not want to enable unnecessary database connections
(2) You want to use a work unit within the limited scope of the UnitOfWorkScope class, as described above
Note: If the unit of work method calls this RemoveFriendship method, it is disabled and it uses the same unit of work as the method that calls it. Therefore, be careful when disabling this function. Similarly, the above program code works well because the warehousing method is a work unit by default.

2. Non-transactional unit of work)

By default, a work unit is transactional (this is its nature ). Therefore, an ABC starts, commits, or rolls back a transaction of an explicit database level. In some special cases, a transaction may cause problems because it may lock some data columns or data tables in the database. In these situations, you may want to disable database-level transactions. The UnitOfWork attribute can obtain a Boolean value from its constructor to make it work like a non-transactional unit of work. Example:

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

We recommend that you do this [UnitOfWork (isTransaction: false)]. (Readable and clear ).

Note that the ORM framework (such as nhib.pdf and EntityFramework) stores data internally in a single command. Suppose you have updated some entities in non-transactional UoW. Even in this situation, all updates are completed at the end of the unit of work of a single database command. However, if you directly execute an SQL query, it will be executed immediately.

There is a non-transactional UoW restriction. If you are already in the transactional UoW area, setting isTransactional to false will be ignored.

Be careful when using non-transactional UoW, because in most cases, data integration should be transactional. If your method only reads data without changing the data, you can certainly use non-transactional data.

3. Call other work units (A unit of work method callanother)

If a work unit method (a method labeled with the UnitOfWork attribute) calls another work unit method, they share the same connection and transaction. The first method is to manage the connection. The other method is to use it. This is feasible when all methods are executed in the same thread (or in the same Web request ). In fact, when the work unit area starts, all program code will be executed in the same thread and share the same connection transaction until the work unit area ends. This is the same for the UnitOfWork attribute and the UnitOfWorkScope class. If you create a different thread/task, it uses its own unit of work.

Automated saving changes (Automatically saving changes)

When we use a work unit to the method, the ABP automatically stores all changes at the end of the method. Suppose we need a method to update the person name:

 [UnitOfWork]  public void UpdateName(UpdateNameInput input) {   var person = _personRepository.Get(input.PersonId);   person.Name = input.NewName;  }

In this way, the name is modified! We didn't even call the _ personRepository. Update method. The ORM framework keeps track of all changes in the entity within the unit of work and reflects all changes to the database.

Note that you do not need to declare UnitOfWork in the application service, because they use work units by default.

4. The GetAll () method of the warehousing interface (IRepository. GetAll ())

When you call the GetAll method outside the warehouse method, there must be an enabled database connection because it returns IQueryable objects. This is required because IQueryable delays execution. It does not immediately execute database queries until you call the ToList () method or use IQueryable in the foreach loop (or access the queried result set ). Therefore, when you call the ToList () method, the database connection must be enabled. Example:

[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, the SearchPeople method must be a unit of work, because IQueryable must be enabled when the ToList () method is called in the method body and the database connection must be enabled when IQueryable. ToList () is executed.

Like the GetAll () method, you must use a work unit if you need to connect to a database without warehouse. Note that the application service method is a work unit by default.

5. UnitOfWork attribute restrictions)

In the following situations, you can use the UnitOfWork attribute Tag:

(1) All interface-based methods such as public or public virtual (for example, an application service is based on a service interface)
(2) self-injection public virtual methods (such as MVC Controller and Web API Controller)
(3) All protected virtual methods.
We recommend that you mark the method as virtual. You cannot apply it to the private method. The private method cannot be implemented by using an inherited method. When you do not use dependency injection and initialize the class on your own, the UnitOfWork attribute (and any proxy) will not work properly.

Option
There are many options that can be used to control the work unit.

First, we can change all default values of all work units in startup configuration. This is usually implemented using the PreInitialize method in our module.

 

public class SimpleTaskSystemCoreModule : AbpModule{  public override void PreInitialize()  {    Configuration.UnitOfWork.IsolationLevel = IsolationLevel.ReadCommitted;    Configuration.UnitOfWork.Timeout = TimeSpan.FromMinutes(30);  }  //...other module methods}

Method
The operating unit system is seamless and invisible. However, in some special cases, you need to call its method.

SaveChanges:

You do not need to do anything. However, sometimes you may want to store all changes in the unit of work. In this case, you can inject IUnitOfWorkManager and call the IUnitOfWorkManager. Current. SaveChanges () method. In this example, the Entity Framework obtains the Id of the newly added object when saving the changes. Note that the current unit of work is transactional, and all changes in the transaction will be rolled back when an exception occurs, even if SaveChange has been called.

Event
The unit of work has a Completed/Failed/Disposed event. You can register these events and perform the required operations. Inject IUnitOfWorkManager and use the IUnitOfWorkManager. Current attribute to obtain the currently activated work unit and register its events.

You may want to execute some program code to successfully complete the current work unit. 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);}

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.