C#【EF Core框架】进行数据库事务处理

3 篇文章 0 订阅

 

上一篇 C#【EF Core框架】使用乐观锁处理并发冲突https://blog.csdn.net/lichao19897314/article/details/122881049

下一篇 C#【EF Core框架】实现高效查询icon-default.png?t=M0H8https://blog.csdn.net/lichao19897314/article/details/122882226

使用事务

  事务允许以原子方式处理多个数据库操作。 如果已提交事务,则所有操作都会成功应用到数据库。 如果已回滚事务,则所有操作都不会应用到数据库。

默认事务行为

   默认情况下,如果数据库提供程序支持事务,则会在事务中应用对 SaveChanges 的单一调用中的所有更改。 如果其中有任何更改失败,则会回滚事务且所有更改都不会应用到数据库。 这意味着,SaveChanges 可保证完全成功,或在出现错误时不修改数据库。

对于大多数应用程序,此默认行为已足够。 如果应用程序要求被视为有必要,则应该仅手动控制事务。

控制事务

  可以使用 DbContext.Database API 开始、提交和回滚事务。 以下示例显示了在单个事务中执行的两个 SaveChanges 操作以及一个 LINQ 查询:

using var context = new BloggingContext();
using var transaction = context.Database.BeginTransaction();

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

    context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/visualstudio" });
    context.SaveChanges();

    var blogs = context.Blogs
        .OrderBy(b => b.Url)
        .ToList();

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

虽然所有关系数据库提供程序都支持事务,但在调用事务 API 时,可能会引发其他提供程序类型或不执行任何操作。

保存点

EF Core 5.0 中已引入此功能。

   如果调用 SaveChanges 且事务已在上下文中进行,则在保存任何数据之前,EF 会自动创建保存点。 保存点是数据库事务中的点,如果发生错误或出于任何其他原因,可能会回滚到这些点。 如果 SaveChanges 遇到任何错误,则会自动将事务回滚到保存点,使事务处于相同状态,就好像从未开始。 这样可以解决问题并重试保存,尤其是在出现乐观并发问题时。

 警告

保存点与 SQL Server 的多重活动结果集不兼容,因此不使用。 如果在 SaveChanges过程中出现错误,则该事务可能处于未知状态。

还可以手动管理保存点,就像管理事务一样。 以下示例在事务中创建保存点,并在失败时回滚到该保存点:

using var context = new BloggingContext();
using var transaction = context.Database.BeginTransaction();

try
{
    context.Blogs.Add(new Blog { Url = "https://devblogs.microsoft.com/dotnet/" });
    context.SaveChanges();

    transaction.CreateSavepoint("BeforeMoreBlogs");

    context.Blogs.Add(new Blog { Url = "https://devblogs.microsoft.com/visualstudio/" });
    context.Blogs.Add(new Blog { Url = "https://devblogs.microsoft.com/aspnet/" });
    context.SaveChanges();

    transaction.Commit();
}
catch (Exception)
{
    // If a failure occurred, we rollback to the savepoint and can continue the transaction
    transaction.RollbackToSavepoint("BeforeMoreBlogs");

    // TODO: Handle failure, possibly retry inserting blogs
}

跨上下文事务

还可以跨多个上下文实例共享一个事务。 此功能仅在使用关系数据库提供程序时才可用,因为该提供程序需要使用特定于关系数据库的 DbTransaction 和 DbConnection

若要共享事务,上下文必须共享 DbConnection 和 DbTransaction

允许在外部提供连接

共享 DbConnection 需要在构造上下文时向其中传入连接的功能。

允许在外部提供 DbConnection 的最简单方式是,停止使用 DbContext.OnConfiguring 方法来配置上下文并在外部创建 DbContextOptions,然后将其传递到上下文构造函数。

 提示

DbContextOptionsBuilder 是在 DbContext.OnConfiguring 中用于配置上下文的 API,现在即将在外部使用它来创建 DbContextOptions

public class BloggingContext : DbContext
{
    public BloggingContext(DbContextOptions<BloggingContext> options)
        : base(options)
    {
    }

    public DbSet<Blog> Blogs { get; set; }
}

替代方法是继续使用 DbContext.OnConfiguring,但接受已保存并随后在 DbContext.OnConfiguring 中使用的 DbConnection

C#复制

public class BloggingContext : DbContext
{
    private DbConnection _connection;

    public BloggingContext(DbConnection connection)
    {
      _connection = connection;
    }

    public DbSet<Blog> Blogs { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(_connection);
    }
}

共享连接和事务

现在可以创建共享同一连接的多个上下文实例。 然后使用 DbContext.Database.UseTransaction(DbTransaction) API 在同一事务中登记两个上下文。

using var connection = new SqlConnection(connectionString);
var options = new DbContextOptionsBuilder<BloggingContext>()
    .UseSqlServer(connection)
    .Options;

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

    using (var context2 = new BloggingContext(options))
    {
        context2.Database.UseTransaction(transaction.GetDbTransaction());

        var blogs = context2.Blogs
            .OrderBy(b => b.Url)
            .ToList();
    }

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

使用外部 DbTransactions(仅限关系数据库)

如果使用多个数据访问技术来访问关系数据库,则可能希望在这些不同技术所执行的操作之间共享事务。

以下示例显示了如何在同一事务中执行 ADO.NET SqlClient 操作和 Entity Framework Core 操作。

using var connection = new SqlConnection(connectionString);
connection.Open();

using var transaction = connection.BeginTransaction();
try
{
    // Run raw ADO.NET command in the transaction
    var command = connection.CreateCommand();
    command.Transaction = transaction;
    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.Database.UseTransaction(transaction);
        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
    transaction.Commit();
}
catch (Exception)
{
    // TODO: Handle failure
}

使用 System.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
    }
}

还可以在显式事务中登记。

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

System.Transactions 的限制

  1. EF Core 依赖数据库提供程序以实现对 System.Transactions 的支持。 如果提供程序未实现对 System.Transactions 的支持,则可能会完全忽略对这些 API 的调用。 SqlClient 支持它。

  2. System.Transactions 的 .NET Core 实现当前不包括对分布式事务的支持,因此不能使用 TransactionScope 或 CommittableTransaction 来跨多个资源管理器协调事务。 支持由此问题跟踪。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在使用 Entity Framework CoreEF Core进行数据库操作时,你不需要显式打开或关闭数据库连接。EF Core 会自动管理数据库连接的打开和关闭。 要使用 SQL 更新数据,你可以使用 EF Core 提供的 `ExecuteSqlRaw` 或 `ExecuteSqlInterpolated` 方法。这些方法允许你执行原始的 SQL 查询或命令。 下面是一个示例代码,展示了如何使用 EF Core 执行原始 SQL 更新数据: ```csharp using Microsoft.EntityFrameworkCore; using System; // 创建 DbContext 类 public class YourDbContext : DbContext { public YourDbContext(DbContextOptions<YourDbContext> options) : base(options) { } // DbSet 和其他属性... public DbSet<IPS_Invoice> IPS_Invoices { get; set; } } public class IPS_Invoice { public int IPS_ID { get; set; } public bool BLOCK { get; set; } public DateTime? BLOCKTIME { get; set; } } public class YourRepository { private readonly YourDbContext _dbContext; public YourRepository(YourDbContext dbContext) { _dbContext = dbContext; } public string UpdateBlockTime(IPS_Invoice model) { string message = ""; try { int affectedRows = _dbContext.Database.ExecuteSqlInterpolated($"UPDATE IPS_Invoices SET BLOCK = true, BLOCKTIME = {DateTime.Now} WHERE IPS_ID = {model.IPS_ID}"); if (affectedRows > 0) { message = "True"; } else { message = "No records updated"; } } catch (Exception e) { message = "False"; // 处理异常... } return message; } } ``` 在上述代码中,我们首先创建了一个继承自 `DbContext` 的 `YourDbContext` 类,并定义了需要操作的实体类 `IPS_Invoice`。然后,我们创建了一个名为 `YourRepository` 的仓储类,在该类中使用 `ExecuteSqlInterpolated` 方法执行原始的 SQL 更新操作。 在 `UpdateBlockTime` 方法中,我们使用插值字符串(interpolated string)来构建 SQL 命令,并将其传递给 `ExecuteSqlInterpolated` 方法进行执行。如果更新操作成功影响了一行或多行数据,返回的 `affectedRows` 将大于 0。 请根据你的实际情况修改代码,并确保在使用 EF Core 时按照最佳实践进行数据库操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小耕家的喵大仙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值