关于.NET编程中各种事务的实现

2023/7/6 02:13:31

"### 从数据库事务开始

在很早的以前,我们要实现一个事务通常是基于SQL的数据库事务,一般的通过SQL查询语言来实现,如下所示,同时更新两本书的价格:

<span style=color: blue;>BEGIN TRANSACTION UPDATE <span style=color: teal;>tb_Book <span style=color: blue;>SET <span style=color: teal;>Price<span style=color: gray;>=122 <span style=color: blue;>WHERE <span style=color: magenta;>IDENT_CURRENT<span style=color: gray;>=1001 <span style=color: blue;>UPDATE <span style=color: teal;>tb_Book <span style=color: blue;>SET <span style=color: teal;>Price<span style=color: gray;>=88 <span style=color: blue;>WHERE <span style=color: magenta;>IDENT_CURRENT<span style=color: gray;>=1002 <span style=color: blue;>IF <span style=color: magenta;>@@ERROR <span style=color: gray;><> 0 <span style=color: blue;>BEGIN ROLLBACK TRANSACTION END ELSE COMMIT TRANSACTION

ADO.NET事务和分布式事务

随着.NET技术的不断发展,可通过ADO.NET来实现,这样我们可以将事务更好的应用在业务逻辑中,而非数据库存储中,这样能够更好的实现业务和存储分离,使的事务被业务逻辑所控制,数据库专注于数据存储,达到各负其责的作用,在ADO.NET中事务的写法是:

<span style=color: blue;>using (<span style=color: #2b91af;>DbTransaction transaction = connection.BeginTransaction()) { command.Transaction = transaction; <span style=color: blue;>try { command.ExecuteNonQuery(); transaction.Commit(); } <span style=color: blue;>catch (<span style=color: #2b91af;>Exception e) { transaction.Rollback(); <span style=color: blue;>throw; } }

上面的代码只能基于同一个数据库连接进行,后来为了实现远程多个数据库的事务,提出了分布式事务,夸数据库服务器进行事务关联:

<span style=color: blue;>using (<span style=color: #2b91af;>TransactionScope transactionScope = <span style=color: blue;>new <span style=color: #2b91af;>TransactionScope()) { <span style=color: green;>//操作数据库服务器1中的数据库 <span style=color: blue;>using (<span style=color: #2b91af;>SqlConnection connection = <span style=color: blue;>new <span style=color: #2b91af;>SqlConnection(connectionString1)) { <span style=color: #2b91af;>SqlCommand command = <span style=color: blue;>new <span style=color: #2b91af;>SqlCommand(commandText1, connection); connection.Open(); command.ExecuteNonQuery(); connection.Close(); }

<span style=""color: green;"">//操作数据库服务器2中的数据库
</span><span style=""color: blue;"">using </span>(<span style=""color: #2b91af;"">SqlConnection </span>connection = <span style=""color: blue;"">new </span><span style=""color: #2b91af;"">SqlConnection</span>(connectionString2))
{
    <span style=""color: #2b91af;"">SqlCommand </span>command = <span style=""color: blue;"">new </span><span style=""color: #2b91af;"">SqlCommand</span>(commandText2, connection);
    connection.Open();
    command.ExecuteNonQuery();
    connection.Close();
}

transactionScope.Complete();

}

基于文件系统的事务(TXF)

有时候,我们需要将NTFS文件系统的操作进行事务管理,例如:首先将图片保存到硬盘,然后将文件路径存入数据库,这两个步骤是满足事务(ACID)原则的,数据库应该与文件系统保持同步,要么都删除,要么都存在,同生共死。但可惜微软没有在.NET中提供这样的API接口供开发人员使用,零度博主抱着对技术精益求精的态度进行了一番的折腾,终于在国外的一个社区找到了针对操作系统内核(kernel32.dll)封装的KTM事务管理方案,零度博主针对自己的需求进行了改进,本方案的C#.NET调用方式如下:

<span style=color: blue;>using (<span style=color: #2b91af;>TransactionScope transactionScope = <span style=color: blue;>new <span style=color: #2b91af;>TransactionScope()) { <span style=color: blue;>string commandText = <span style=color: #a31515;>UPDATE Book SET Price=88.50 WHERE ID=1001;

<span style=""color: blue;"">var </span>fileStream = <span style=""color: #2b91af;"">TransactedFile</span>.Open(<span style=""color: #a31515;"">@""log.txt""</span>, <span style=""color: #2b91af;"">FileMode</span>.OpenOrCreate, <span style=""color: #2b91af;"">FileAccess</span>.Write, <span style=""color: #2b91af;"">FileShare</span>.Write);
<span style=""color: #2b91af;"">StreamWriter </span>streamWrite = <span style=""color: blue;"">new </span><span style=""color: #2b91af;"">StreamWriter</span>(fileStream);
streamWrite.WriteLine(<span style=""color: blue;"">string</span>.Concat(<span style=""color: #2b91af;"">DateTime</span>.Now, commandText));
streamWrite.Flush();
streamWrite.Close();

<span style=""color: #2b91af;"">SqlConnection </span>connection = <span style=""color: blue;"">new </span><span style=""color: #2b91af;"">SqlConnection</span>(connectionString);
<span style=""color: #2b91af;"">SqlCommand </span>command = <span style=""color: blue;"">new </span><span style=""color: #2b91af;"">SqlCommand</span>(commandText, connection);
connection.Open();
command.ExecuteNonQuery();
connection.Close();

transactionScope.Complete();

}

以上示例代码首先创建TransactionScope事务环境,将文件操作和数据库操作关联成成同一个事务,TransactedFile正是上文提到的基于KTM的本地事务封装,数据库事务本身支持隐式自动关联,当前者和后者同时被关联到TransactionScope事务上下文环境后,就形成了一个完成的事务。上面的示例首先向磁盘的log.txt中写入当前的时间和要执行的SQL语句,然后通过SQL更新Book价格,如果更新价格失败则回滚写入的日志(自动删除日志)。

总之:事务是一个复杂的系统,主要有KTM、DTC和LTM事务,内部实现及其复杂,本文主要简单说明事务的基本用法,针对原理性的研究请关注零度博客未来的文章,感谢您的阅读,希望对您有所帮助!

附基于文件系统TXF的本地事务封装包:KtmIntegration"