使用Dapper .NET进行事务处理

3

我目前在一个层中有两个类,它们负责将数据插入数据库:

using Dapper;
using System;
using System.Data.SqlClient;
using System.Linq;

namespace repositories
{
    public class DAOBook
    {
        private readonly string _connection;

        public DAOBook(string databaseConnection)
        {
            _connection = databaseConnection;
        }

        public bool IncludeBook(string title)
        {
            try
            {
                using (var connection = new SqlConnection(_connection))
                {
                    var sql = $@"
                                INSERT INTO books
                                  (title)
                                VALUES
                                  ('{title}' ";

                    var result = connection.Execute(sql);

                    return result != 0;
                }
            }
            catch (Exception ex)
            {
                throw new Exception($"{ex.Message}", ex);
            }
        }
    }
}

using Dapper;
using System;
using System.Data.SqlClient;
using System.Linq;

namespace repositories
{
    public class DAOBookTag
    {
        private readonly string _connection;

        public DAOBookTag(string databaseConnection)
        {
            _connection = databaseConnection;
        }

        public bool IncludeBookTag(string tag, int userid)
        {
            try
            {
                using (var connection = new SqlConnection(_connection))
                {
                    var sql = $@"
                                INSERT INTO bookTag
                                  (tag, userid)
                                VALUES
                                  ('{tag}', {userid} ";

                    var result = connection.Execute(sql);

                    return result != 0;
                }
            }
            catch (Exception ex)
            {
                throw new Exception($"{ex.Message}", ex);
            }
        }
    }
}

在我的服务层中,我可以正常调用这两个类,并将它们插入到数据库中。
try
{
    var connectionString = "<my_connection_string>";
    var daoBook = new DAOBook(connectionString);
    var daoBookTag = new DAOBookTag(connectionString);

    dao.IncludeBook("Alice");
    dao.IncludeBookTag("Romance", 1);
}
catch (Exception ex)
{
    throw new Exception($"{ex.Message}", ex);
}

然而,我想要加入一个事务控制,以便在插入第二个类时出现错误时,在catch中撤销该事务,就像这样:
try
{
    var connectionString = "<my_connection_string>";
    var daoBook = new DAOBook(connectionString);
    var daoBookTag = new DAOBookTag(connectionString);

    // begin transaction    
    dao.IncludeBook("Alice");
    dao.IncludeBookTag("Romance", 1);
    // commit
}
catch (Exception ex)
{
    // rollback
    throw new Exception($"{ex.Message}", ex);
}

我知道这可能是一个初学者的问题,但我似乎找不到两个持久化类共享同一事务的方法。

我看到了一个实现Dapper事务控制的例子,但我不知道如何在我的服务层中实现它(而不是持久化层)。 https://riptutorial.com/dapper/example/22536/using-a-transaction

谢谢


1
如果书名是bobby tables,那该怎么办呢?...无论如何,显然你现在的代码无法做到这一点,因为你需要将相同的连接和事务传递到某个地方。 - Selvin
1
看看这个 Dapper 的工作单元 是否对你有帮助。 - Amit Joshi
2
重要提示:请注意,您的SQL存在巨大的注入漏洞;Dapper使参数化查询变得“无痛” - 请充分利用它! - Marc Gravell
2个回答

7
在ADO.NET中处理事务有两种方式;通常首选的机制是使用ADO.NET事务,即BeginTransaction。它具有一定的限制,但非常高效并且可以原生地映射到大多数提供程序。ADO.NET事务的关键限制是它仅跨越一个连接,并且您的连接必须至少持续与事务相同的时间。
在使用Dapper时,您必须将事务传递到调用中;例如:
using (var conn = new SqlConnection(connectionString))
{
    connection.Open();
    using (var tran = connection.BeginTransaction())
    {
        // ... your work
        tran.Commit();
    }
}

在这里,“你的工作”有效地使用相同的conntran实例,使用:

var result = conn.Execute(sql, args, transaction: tran);

一种更为简单的方法是使用 TransactionScope。这种方法使用起来更加方便,但也更加复杂。虽然它能够正常工作,但我通常会建议不要使用。


你还应该使用参数化查询:

var sql = @"
INSERT INTO bookTag (tag, userid)
VALUES (@tag, @userId)";
var result = connection.Execute(sql, new { tag, userId });

1
嗨马克,你提供的建议很不错。但是为什么你在trans.Rollback()中没有使用catch块呢?就像Dapper教程网站上的一些示例所展示的那样https://riptutorial.com/dapper/example/22536/using-a-transaction#example。这是可选的吗?谢谢。 - deveton

3

使用TransactionScope

using (var transactionScope = new TransactionScope())
{
    var connectionString = "<my_connection_string>";
    var daoBook = new DAOBook(connectionString);
    var daoBookTag = new DAOBookTag(connectionString);

    // begin transaction    
    dao.IncludeBook("Alice");
    dao.IncludeBookTag("Romance", 1);
    //commit
    transactionScope.Complete();
}

https://dapper-tutorial.net/transaction


6
我通常会不建议使用TransactionScope,除非有非常好的理由,而我认为这不是一个很好的理由;同时请注意,该网站并不是官方的dapper指南 - 它与dapper的作者/项目没有任何关系。 - Marc Gravell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接