[Yugong Series] September 2023.NET/C# knowledge points-EF affairs and environmental affairs


Preface

A transaction refers to a series of operations, which are either all executed successfully or not executed at all. Partial execution is successful and partial execution fails. The role of transactions is to ensure data consistency, ensure that the execution results of multiple operations are atomic, and ensure data integrity and correctness.

Distributed transactions refer to transactions across multiple nodes or multiple databases in a distributed system. In a distributed environment, since data is scattered across multiple nodes, it is necessary to ensure the consistency of the entire distributed transaction, that is, all nodes will either execute successfully or not execute at all. Therefore, the significance of distributed transactions is to ensure the consistency, reliability and integrity of data, prevent data loss and duplication, and achieve high availability and high performance of distributed systems.

There are many ways to implement distributed transactions, including two-phase commit protocols, three-phase commit protocols, compensation transactions, etc. Among them, the two-phase commit protocol is the most classic implementation method. It ensures the consistency of the entire distributed transaction through three stages: pre-commit, commit and rollback. However, there are also some problems, such as poor performance and scalability. . Therefore, in an actual distributed system, it is necessary to choose an appropriate distributed transaction implementation method according to specific scenarios and requirements.

1. EF affairs and environmental affairs

1.EF affairs

In EF, BeginTransaction is a method of starting a transaction, which allows us to perform multiple operations within a transaction scope and ensure that these operations are either all executed successfully or all rolled back.

The steps to use the BeginTransaction method are as follows:

  1. Create a DbContext object, which is the data context in EF;
  2. Instantiate a DbContextTransaction object, which represents a transaction;
  3. Call the Database.BeginTransaction() method of DbContext and return a DbContextTransaction object, indicating the start of a transaction;
  4. Perform operations within the transaction scope that require transaction support;
  5. Call the Commit() method of DbContextTransaction to commit the transaction, or call the Rollback() method to roll back the transaction.

The following is an example of using the BeginTransaction method to start a transaction:

using (var db = new MyDbContext())
{
    
    
    using (var transaction = db.Database.BeginTransaction())
    {
    
    
        try
        {
    
    
            //在事务范围内执行需要支持事务的操作,比如:
            db.Customers.Add(new Customer {
    
     Name = "Alice" });
            db.Orders.Add(new Order {
    
     CustomerId = 1, TotalPrice = 100 });
            db.SaveChanges();

            //提交事务
            transaction.Commit();
        }
        catch (Exception ex)
        {
    
    
            //发生异常时回滚事务
            transaction.Rollback();
            Console.WriteLine("Error: " + ex.Message);
        }
    }
}

In this example, we first create a MyDbContext object, then use the BeginTransaction method to start a transaction, and finally call the Commit method to commit the transaction, or call the Rollback method to roll back the transaction when an exception occurs.

It should be noted that when using the BeginTransaction method to start a transaction, be sure to call the Commit method to submit the transaction or the Rollback method to roll back the transaction after the operation is completed, otherwise data inconsistency will occur.

2. Environmental Affairs

2.1 TransactionScope

System.Transactions is a namespace used to implement environmental transactions in C#. Environmental transactions (also known as distributed transactions) are transactions involving multiple data sources (such as multiple databases, message queues, etc.) and require consistency across multiple data sources. System.Transactions provides a simple, reliable, and scalable way to handle this type of transaction.

System.Transactions is a transaction processing mechanism based on .NET Framwork. It provides the TransactionScope class to manage transactions and is compatible with multiple data sources, such as ADO.NET, Message Queue, etc.

The steps to use System.Transactions are as follows:

  1. Create a TransactionScope object, which represents the environment transaction;
  2. Perform operations within the scope of the TransactionScope object that need to support environmental transactions;
  3. Call the Complete() method of the TransactionScope object to commit the transaction, or let the code exit the TransactionScope code block without calling the Complete method. At this time, the transaction will be automatically rolled back.

The following is an example of using the TransactionScope object to manage transactions:


using (var scope = new TransactionScope(
    TransactionScopeOption.Required,
    new TransactionOptions {
    
     IsolationLevel = IsolationLevel.ReadCommitted }))
{
    
    
    using var connection = new SqlConnection(connectionString);
    connection.Open();

    try
    {
    
    
        // Run raw ADO.NET command in the transaction
        var command = connection.CreateCommand();
        command.CommandText = "DELETE FROM dbo.Blogs";
        command.ExecuteNonQuery();

        // Run an EF Core command in the transaction
        var options = new DbContextOptionsBuilder<BloggingContext>()
            .UseSqlServer(connection)
            .Options;

        using (var context = new BloggingContext(options))
        {
    
    
            context.Blogs.Add(new Blog {
    
     Url = "http://blogs.msdn.com/dotnet" });
            context.SaveChanges();
        }

        // Commit transaction if all commands succeed, transaction will auto-rollback
        // when disposed if either commands fails
        scope.Complete();
    }
    catch (Exception)
    {
    
    
        // TODO: Handle failure
    }
}

In this example, we use the TransactionScope object to manage environmental transactions, then perform the operations on the data sources represented by the two SqlConnection objects in the TransactionScope code block, and finally call the Complete method to commit the transaction.

It should be noted that when using the TransactionScope object to manage transactions, you must ensure that operations performed within the transaction scope support transactions, otherwise data inconsistency may occur. In addition, when using the TransactionScope object to manage transactions, you also need to ensure that the data source supports distributed transactions, otherwise a NotSupportedException will be thrown.

2.2 CommittableTransaction

CommittableTransaction is a transaction mechanism provided by the .net framework, which can be used to control the consistency of multiple resources (such as databases and file systems) during transactional operations.

CommittableTransaction is provided through the System.Transactions namespace, which provides the following important methods:

  1. Begin: Create a new CommittableTransaction object.
  2. Commit: Submit the transaction to make it effective.
  3. Rollback: Roll back the transaction and cancel the previous operation.
  4. EnlistResource: Add a resource to the transaction.

When using CommittableTransaction, you generally need to follow these steps:

  1. Create a CommittableTransaction object.
  2. Add the resources that need to participate in the transaction to the transaction.
  3. Perform an operation (such as an update to a database or a write to a file system).
  4. If the operation is successful, call the Commit method to commit the transaction.
  5. If the operation fails, call the Rollback method to roll back the transaction.

The following is a sample code using CommittableTransaction:

using (var transaction = new CommittableTransaction(
    new TransactionOptions {
    
     IsolationLevel = IsolationLevel.ReadCommitted }))
{
    
    
    var connection = new SqlConnection(connectionString);

    try
    {
    
    
        var options = new DbContextOptionsBuilder<BloggingContext>()
            .UseSqlServer(connection)
            .Options;

        using (var context = new BloggingContext(options))
        {
    
    
            context.Database.OpenConnection();
            context.Database.EnlistTransaction(transaction);

            // Run raw ADO.NET command in the transaction
            var command = connection.CreateCommand();
            command.CommandText = "DELETE FROM dbo.Blogs";
            command.ExecuteNonQuery();

            // Run an EF Core command in the transaction
            context.Blogs.Add(new Blog {
    
     Url = "http://blogs.msdn.com/dotnet" });
            context.SaveChanges();
            context.Database.CloseConnection();
        }

        // Commit transaction if all commands succeed, transaction will auto-rollback
        // when disposed if either commands fails
        transaction.Commit();
    }
    catch (Exception)
    {
    
    
        // TODO: Handle failure
    }
}

In the above example, we use CommittableTransaction to control the transaction of a database update operation. If the operation is successful, we will commit the transaction, otherwise roll back the transaction.

2.3 The difference between CommittableTransaction and TransactionScope

CommittableTransaction and TransactionScope are both classes provided in the .NET Framework for managing transactions. The main difference between them lies in the method of use and the granularity of transaction management.

CommittableTransaction is a transaction object that is explicitly created and managed in code. It requires developers to manually specify the resources that need to be managed and when to commit or rollback the transaction. Typically, CommittableTransaction is used when executing custom transaction management logic in code.

TransactionScope is a higher-level transaction management class that can automatically manage transactional operations of multiple transactional resources. When using TransactionScope, developers only need to put the code blocks that need to be executed in the same transaction in a TransactionScope object. The management and submission or rollback of the transaction are automatically completed by the TransactionScope object instance.

Specifically, CommittableTransaction requires explicit transaction management in the code, and requires manually specifying the resources that the transaction needs to manage and when to commit or rollback the transaction, while TransactionScope provides developers with automatic management of resources and transactional operations. A higher level of transaction management.

It should be noted that TransactionScope also uses CommittableTransaction internally to implement transaction management, but it is encapsulated to make it more convenient and simpler to use.

2.4 Limitations of System.Transactions

System.Transactions has the following limitations:

  1. Can only be used in databases that support distributed transactions. Databases that do not support distributed transactions cannot perform transaction processing through System.Transactions.

  2. In distributed transactions, performance issues may arise due to the need to coordinate the operations of multiple participants.

  3. System.Transactions can only handle database transactions and message queue transactions, and cannot be used for other types of transaction processing, such as file system transactions.

  4. In a distributed transaction, if one of the participants fails, the entire transaction may fail or need to be rolled back. This may result in data loss or anomalies in the application.

  5. In some cases, it may be necessary to manually write distributed transaction processing code due to limitations of System.Transactions. This can lead to more work and complexity.

  6. As of .NET Core 2.1, the System.Transactions implementation does not include support for distributed transactions, so you cannot use TransactionScope or CommittableTransaction to coordinate transactions across multiple resource managers.

Guess you like

Origin blog.csdn.net/aa2528877987/article/details/132872582