业务场景:用户下单à付款à扣减库存
问题:当付款成功,扣减库存失败,如何处理。是否可以将这两个事务放在一个Transaction中处理?
一、基本概念(Transaction)
什么是事务?事务很任性,只接受成功或失败。在事务包括的范围内,所有操作都是不可分割的单元,当所有操作都成功后事务才算执行成功,否则将进行回滚操作。回忆一下课本中的ACID四大属性(了解即可,复制了书中的描述):
A(Atomicity)原子性:不可再分割的整体,事务中的含义是所有操作都被视为同一不可分割的整体,要么都成功要么都失败。
C(Consistence)一致性:事务开始前和事务结束后,数据库的完整性约束没有被破坏。
I(Isolation)隔离性:当数据正处于事务中时是独占的,不能被其它应用所访问,确保不会读取到脏数据。
D(Durability)持久性:事务一旦提交,则物理存储于数据库中,且在下一次操作前,数据的状态不会被改变。
二、解决方案
通过System.TransactionScope 可以实现两个库之间的事务,我们只需要引入System.Transactions.dll 就可以微软提供的强大事务功能了。这里实现的是两个数据库之间的整务,若是要实现数据库和NTFS文件之间的事务,请大家搜索DTC机制,来实现非数据库之间的事务。这部分内容我也正在实践和学习,待理解以后再发出博文与大家探讨。
本实例是连接了本机的两个数据库服务SQLServer2008R2和MySQL,所以需要在工程中引入MySQL.dll。
以下是实现代码,当DoPayment方法执行成功后,Dostock方法执行失败,此时事务不执行transcope.Complete();语句,在SQLServer中的数据会被回滚。并且在执行Dostock方法时,去查询SQLServer中是查询不到数据的。
namespace ConsoleApplication3 { class Program { static string SQLConStr = "server=10.10.60.195,1433;database=tmp;uid=sa;pwd=sa;"; static string MySQLConStr = "server=localhost;uid=root;pwd=root;database=test"; static void Main(string[] args) { OrderLogic(); } public static void OrderLogic() { using (TransactionScope transcope = new TransactionScope()) { //用户付款 //向SQLServer.TPayment表插入一条记录 string sqlText = "insert into TPayment values(156,1,1)"; DoPayment(sqlText); //扣减库存 //更新MySQL.Product表中stocknum字段 string mysqlText = "update Product set stocknum=stocknum-5 where id=1"; DoStock(mysqlText); transcope.Complete(); } } public static void DoPayment(string sqlText) { SqlConnection conn = null; SqlCommand com = null; try { conn = new SqlConnection(SQLConStr); conn.Open(); com = new SqlCommand(sqlText, conn); com.ExecuteNonQuery(); } catch { } finally { if (com != null) com.Dispose(); if (conn != null) conn.Close(); } } public static void DoStock(string sqlText) { MySqlConnection conn = null; MySqlCommand com = null; try { conn = new MySqlConnection(MySQLConStr); conn.Open(); com = new MySqlCommand(sqlText, conn); com.ExecuteNonQuery(); } catch { } finally { if (com != null) com.Dispose(); if (conn != null) conn.Close(); } } } }