事务属性
事务应该具有4个属性:原子性、一致性、隔离性、持续性。这四个属性通常称为ACID特性。
原子性(atomicity)。一个事务是一个不可分割的工作单位,事务中包括的操作作要么都做,要么都不做。
一致性(consistency)。事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
隔离性(isolation)。一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
持久性(durability)。持续性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
隔离级别
● 未授权读取,也称为读未提交(Read Uncommitted):允许脏读取,但不允许更新丢失。如果一个事务已经开始写数据,则另外一个数据则不允许同时进行写操作,但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现。
● 授权读取,也称为读提交(Read Committed):允许不可重复读取,但不允许脏读取。这可以通过“瞬间共享读锁”和“排他写锁”实现。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。
● 可重复读取(Repeatable Read):禁止不可重复读取和脏读取,但是有时可能出现幻影数据。这可以通过“共享读锁”和“排他写锁”实现。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。
● 序列化(Serializable):提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。如果仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。
隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed,它能够避免脏读取,而且具有较好的并发性能。尽管它会导致不可重复读、虚读和第二类丢失更新这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制。
TransactionScope
TransactionScope 类提供了一种简单方法,使您无需与事务自身进行交互,就可以在参与事务时对代码块进行标记。事务范围可以自动选择和管理环境事务。由于 TransactionScope 具有简单易用性和高效性,因此建议您在开发事务应用程序时使用该类。此外,还无需将资源显式登记到事务。任何 System.Transactions 资源管理器(如 SQL Server 2005)都可以检测范围所创建的环境事务是否存在,并自动对其进行登记。
简单使用TransactionScope实现事务
//简单使用
private void Demo1()
{
using (SqlConnection conn = new SqlConnection("Data Source=OWEN//SQL2005;Initial Catalog=TsDB;Persist Security Info=True;User ID=sa;Password=shellwin;"))
{
SqlCommand cmd = new SqlCommand();
SqlParameter[] paras = null;
//创建事务范围
using (TransactionScope ts = new TransactionScope())
{
conn.Open();
//第一个操作
cmd.CommandText = "insert into user_info values(@name1,@password1)";
cmd.CommandType = CommandType.Text;
cmd.Connection = conn;
paras = new SqlParameter[]{
new SqlParameter ("@name1",SqlDbType.VarChar,50),
new SqlParameter ("@password1",SqlDbType .VarChar,50)};
paras[0].Value = "1";
paras[1].Value = "1";
foreach (SqlParameter para in paras)
{
cmd.Parameters.Add(para);
}
cmd.ExecuteNonQuery();
Console.WriteLine("第一个操作执行");
//第二个操作
cmd.CommandText = "insert into user_info values(@name2,@password2)";
cmd.CommandType = CommandType.Text;
cmd.Connection = conn;
paras = new SqlParameter[]{
new SqlParameter ("@name2",SqlDbType.VarChar,50),
new SqlParameter ("@password2",SqlDbType .VarChar,50)};
paras[0].Value = "2";
paras[1].Value = "2";
foreach (SqlParameter para in paras)
{
cmd.Parameters.Add(para);
}
cmd.ExecuteNonQuery();
Console.WriteLine("第二个操作执行");
//第三个操作
cmd.CommandText = "insert into user_info values(@name3,@password3)";
cmd.CommandType = CommandType.Text;
cmd.Connection = conn;
paras = new SqlParameter[]{
new SqlParameter ("@name3",SqlDbType.VarChar,50),
new SqlParameter ("@password3",SqlDbType .VarChar,50)};
paras[0].Value = "3";
paras[1].Value = "3";
foreach (SqlParameter para in paras)
{
cmd.Parameters.Add(para);
}
cmd.ExecuteNonQuery();
Console.WriteLine("第三个操作执行");
//提交事务
ts.Complete();
Console.WriteLine("整个事务提交");
}
}
Console.ReadLine();
}