Parsing the transaction processing and work unit _ practical skills in the ABP framework

Source: Internet
Author: User
Tags connection pooling

Common connection and transaction management methods
connection and transaction management are one of the most important concepts of applications that use databases. When you open a database connection, when to start a transaction, how to release the connection ... and whatnot.

As you all know,. NET uses connection pooling (connection pooling). Therefore, creating a connection is actually getting a connection from the connection pool, because it costs to create a new connection. 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 a release in the real sense. This mechanism is made up of. NET provides. Therefore, we should release the connection object after use. This is the best practice.

In the application, there are two common callers to create/release a database connection:

First Method: Create a Connection object when the Web request arrives. (Application_BeginRequest this event in Global.asax), use the same connection object to handle all database operations, and close/release the connection at the end of the request (Application_ EndRequest event).

This is a simple but inefficient method, because:

    • Perhaps this Web request does not need to manipulate the database, but the connection will open. This is a inefficient way of using the connection pool.
    • This may make the Web request run longer and the database operation will require some execution. This is also a inefficient connection pool usage.
    • This is feasible for Web applications. If your application is Widnows Service, this may not be implemented.
    • This is also the best scenario for using a transactional database operation. If one of the operations fails, all operations are rolled back. Because the transaction locks some data columns (event data tables) in the database, it must be short-lived.

The second method: Create a connection when needed (just before using it) and release it after it is used. This is quite efficient, but it has to be tedious and repetitive (create/release connections).

ABP Connection and transaction management
ABP combines the above two connection management methods and provides a simple and efficient model.

1. Storage class (Repository classes)

Warehousing is the primary database operation of the class. ABP opens a database connection and enables a transaction when entering the warehousing method. Therefore, you can safely use the connection to the warehousing method. After the warehousing method is finished, the transaction is committed and the connection is released. If the warehousing method throws any exceptions, the transaction is rolled back and the connection is released. In this model, the Warehousing method is unit (a unit of work of work). ABP are fully automated in handling the above actions. Here, there is a simple warehousing:

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


This example uses NHibernate as an ORM framework. As shown above, you do not need to compose program code for any database connection operations (sessions in NHibernate).

If the warehousing method calls another warehousing method (generally, if the method of the work unit method calls another unit of work), the same connection and transaction are used. The first called warehousing method is responsible for managing connections and transactions, while the rest of the storage methods that it calls are simply not managed.

2. Application Services (Application service classes)

A method of applying a service is also considered to use a unit of work. If we have an application service method 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 to the warehouse and use the statistics warehouse to increase the total people quantity. Two repositories share the same connection and transactions in this example, because this is a method of applying services. ABP opens a database connection and opens a transaction into the Creationperson method, and if there are no exceptions thrown, and then commits the transaction at the end of the method, the transaction is rolled back if an exception is thrown. In this mechanism, all the operations of the database in the Createperson, have become a unit (unit of work).

3. Work module (unit of work)

The work unit works in the background for warehousing and application service methods. If you want to control the connection and transaction of the database, you need to manipulate the work unit directly. Here are two examples that are used directly:

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 transforms into a unit of work and manages database connections and transactions, with two warehouse objects using the same unit of work. Note that if this is a method of applying a service, you do not need to add the Unitofwork attribute, see Work Unit Method: Chapter Three, 3.3.5.

The second example is to use the Iunitofworkmanager.begin (...) The method looks like this:

 public class MyService {private ReadOnly iunitofworkmanager;
  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 (limited scope) unit of work. In this mechanism, you can usually call the complete method manually. If you do not call, the transaction rolls back and all the exceptions are not stored. The Begin method is overridden to set options for the unit of work.

This is great, but unless you have a good reason, use the unitofwork attribute less.

Work unit
1. Disable unit of work (disabling units of work)

You might want to disable the work unit that applies the service method (because it is enabled by default). To do this, use the Unitofworkattribute isdisabled attribute. Examples are as follows:

[Unitofwork (isdisabled = true)]
public virtual void removefriendship (Removefriendshipinput input)
{
  _friendshiprepository.delete (input). ID);

Normally, you don't need to do this because the method of applying the service should be cellular and typically use a database. In some cases, you may want to disable the work unit for the application service:

(1) Your method does not require any database operations and you do not want to open the database connections that you do not need
(2) You want to use the work unit within a limited range of Unitofworkscope classes, as described above
Note that if the work unit method calls this Removefriendship method, disable the ignored and it and the methods that invoke it use the same unit of work. Therefore, be careful with disabling this feature. Similarly, the above program code works well because the warehouse method defaults to the unit of work.

2. Non-transactional unit of work (non-transactional units of work)

The unit of work is by default transactional (which is its nature). Therefore, ABP initiates/commits/rolls back transactions of a dominant database level. In some special cases, a transaction can cause problems because it may lock up some data columns or data tables in the database. In these situations, you may want to disable database-level transactions. The Unitofwork property can get a Boolean value from its constructor to work like a non transactional unit of work. Examples are:

 [Unitofwork (false)]
Public gettasksoutput gettasks (gettasksinput input)
{
  var tasks = _taskrepository.getallwithpeople (input. Assignedpersonid, input. State);
  return new Gettasksoutput
      {
        tasks = mapper.map<list<taskdto>> (tasks)
      };

It is recommended that you do so [Unitofwork (Istransaction:false)]. (Readable and unambiguous).

Note that ORM frameworks (like NHibernate and EntityFramework) store data internally in a single command. Suppose you have updated some of the entities that are not transactional UOW. Even in this scenario, all updates are done at the end of the work unit of a single database command. However, if you execute the SQL query directly, it will be executed immediately.

There is a non-transactional UOW limit here. If you are already in a transactional UOW area, setting IsTransactional to False will be ignored.

Be careful with non-transactional UOW, because in most cases, data consolidation should be transactional. If your method simply reads the data and does not change the data, then of course you can use non-transactional.

3. Work unit calls other work units (A unit of work method calls another)

If the work unit method (a method labeled with the Unitofwork property tag) invokes another unit method, they share the same connection and transaction. The first method manages the connection, and other methods simply use it. This is possible for all methods to execute under the same thread (or within the same Web request). In fact, when the work cell area begins, all program code executes and shares the same connection transaction in the same thread until the unit area of work terminates. This is the same for using the Unitofwork property and the Unitofworkscope class. If you create a different thread/task, it uses the unit of work that it belongs to.

Automated saving changes (automatically saving changes)

When we use the work unit to the method, the ABP automatically stores all the changes at the end of the method. Let's say we need a way 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 has been modified! We didn't even call the _personrepository.update method. The ORM framework keeps track of all changes in the entity's work units and reflects all changes to the database.

Note that this does not need to be unitofwork in the application service declaration because they are by default a unit of work.

4. GetAll () Method of Warehousing interface (Irepository.getall ())

When you use the GetAll method in the warehousing method, this must have an open database connection because it returns the object of the IQueryable type. This is required because the IQueryable is deferred for execution. It does not immediately execute a database query until you invoke the ToList () method or use IQueryable in the Foreach loop (or access the result set of the query). Therefore, when you invoke 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 is invoked ToList () method in the method ontology, and the database connection must be opened when Iqueryable.tolist () is executed.

As with the GetAll () method, you must use a work unit if a database connection is required and there is no storage. Note that the application service method defaults to the unit of work.

5. Limitations of Working Unit attributes (unitofwork attribute restrictions)

In the following scenario you can use the Unitofwork attribute tag:

(1) class all public or public virtual these interface based methods (such as application services are based on the service interface)
(2) The public virtual method of the Self injecting class (like MVC Controller and Web API Controller)
(3) All protected virtual methods.
It is recommended that you mark the method as virtual. You can't apply it to private methods. Because ABP is implemented using dynamic Proxy, private methods cannot be implemented using inherited methods. When you do not use dependency injection and initialize the class yourself, the Unitofwork attribute (and any proxy) will not function properly.

Options
There are many options that you can use to control the unit of work.

First, we can change all the default values for all the work units in startup configuration. This is usually accomplished 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 ();

  ... other module Methods
}

Method
The work unit system works seamlessly and is not visible. However, in some special cases, you need to call its method.

SaveChanges:

ABP Store all the changes in the end of the work unit, you don't have to do anything. Sometimes, however, you might want to store all the changes in the work unit process. In this case, you can inject Iunitofworkmanager and invoke the IUnitOfWorkManager.Current.SaveChanges () method. In the example, the Entity Framework gets the ID of the new entity when the change is stored. Note that the current unit of work is transactional, and all changes in the transaction are rolled back when the exception occurs, even if the savechange has been invoked.

Event
The unit of work has a completed/failed/disposed event. You can register these events and do the required actions. Inject Iunitofworkmanager and use the Iunitofworkmanager.current property to get the currently activated unit of work and register its events.

You might want to perform some program code successfully completed in 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);
}

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.