1、事务处理3. .NET 2.0中的事务 通过登记正在执行的事务性工作类型的资源管理器,.NET Framework 2.0中的事务管理系统能够解决动态事务组合而导致的额外开销问题。它还提供了将多个不稳定资源转换为提交和回滚事务模型所需的架构。在下面的内容中将介绍.NET 2.0中与事务有关的轻量级事务管理、显式事务、TransactionScope类和自动化事务。 轻量级事务管理 对于发生在单个应用程序域中的事务,LTM是一种运行很快,非常便宜的资源管理器。LTM是框架中所有事务的起点,同时监视正在与事务交互的资源,以及根据需要登记更多健壮的事务管理器的服务。 当事务性工作在进程外工作(例如开
2、始修改数据库数据)时,LTM将自动使用支持可升级的单阶段登记(PSPE)的资源管理器模型。这是一种新的事务性架构,其理解LTM的“预付费”机制。如果没有可用的PSPE管理器,那么LTM会登记DTC。当然,多个远程数据源将被登记的DTC修改。当PSPE模型开始工作时,事务的执行将于ADO.NET 1.x的事务一致。读者可能会怀疑PSPE具有与ADO.NET事务一样的性能。当与多个数据库交互时,事务将自动提升到DTC。 在.NET Framework 2.0中,使用SQL Server 2005时将自动获得PSPE事务。如果事务性工作与另一个服务器或者数据库交互,那么自动使用DTC。易失性事务自动
3、参与PSPE,而不用调用DTC。 实现显式事务 有时候,TransactionScope对象的默认隐式自动事务功能可能无法提供所需较好的控制级别。在这种情况下,可能需要人工创建事务,同时显式提交或者回滚事务。示例1显示使用CommittableTransaction类创建显式事务包括的步骤。 例1:使用CommittableTransaction实现显式事务 void btnSave_Click(object sender, EventArgs e) CommittableTransaction trans = new CommittableTransaction(); try string
4、connectionString = WebConfigurationManager.ConnectionStrings Mydatabase.ConnectionString; using (SqlConnection connection = new SqlConnection(connectionString) string sql = Insert into Production.ProductCategory(Name, + rowguid, ModifiedDate) Values(Name, rowguid, ModifiedDate); / 打开连接,在事务范围中登记此连接 c
5、onnection.Open(); SqlCommand command = new SqlCommand(sql, connection); command.CommandType = CommandType.Text; SqlParameter nameParam = new SqlParameter(Name, SqlDbType.NVarChar, 50); nameParam.Value = txtCategoryName.Text; command.Parameters.Add(nameParam); SqlParameter guidParam = new SqlParamete
6、r(rowguid, SqlDbType.UniqueIdentifier); guidParam.Value = System.Guid.NewGuid(); command.Parameters.Add(guidParam); SqlParameter modifieDateParam = new SqlParameter(ModifiedDate, SqlDbType.DateTime); modifieDateParam.Value = System.DateTime.Now; command.Parameters.Add(modifieDateParam); /在当前事务的范围中登记
7、事务 connection.EnlistTransaction(trans); command.ExecuteNonQuery(); / 如果每一个执行都成功,则提交事务 trans.Commit(); lblResult.Text = Category is written successfully; catch (Exception ex) / 如果出现异常,则回滚事务 trans.Rollback(); lblResult.Text = Exception is : + ex.Message; Using Explicit Transactions using CommittableTr
8、ansaction 在这种方法中,需要调用SqlConnection对象的EnlistTransaction()方法(传递CommittableTransaction对象作为参数),以便将SqlConnection对象与CommittableTransaction对象关联起来。一旦完成这个工作,然后就可以通过调用CommittableTransaction对象的Commit()和Rollback()方法,显式提交或者回滚事务。正如能够想象的,不推荐使用这种手动方法,因为当发生不同类型的异常时,可能会遇到一些无法回滚事务的风险。 使用TransactionScope类 正如名称所暗示,Trans
9、actionScope类用于限定事务代码块,其具有一些明显优点,例如范围与应用程序对象模型无关,同时提供了一个简单直观的编程模型等等。在该类的构造函数内部,TransactionScope对象创建了一个事务(.NET 2.0中默认时轻量级事务管理器),同时将该事务设置给Transaction类的Current属性。由于TransactionScope是可释放对象,所以事务将调用Dispose()方法释放该对象: using(TransactionScope scope = new TransactionScope() /*在这里实现事务性工作 */ / 没有错误提交事务 scope.Compl
10、ete(); 示例2列举了一种在.NET 2.0中创建事务的方法。在TransactionScope对象定义的代码块中创建和释放该对象。使用TransactionScope对象的构造函数和TransactionScopeOption枚举,开发人员能够定义是否需要新事务,或者是否应该使用已经在外部块中存在的事务。TransactionScope.Complete()方法指示事务范围内的所有操作都已成功完成。在using语句结尾处(调用Dispose()方法的位置),定义了事务块的输出。如果由于发生异常而没有调用Complete()方法,那么放弃事务。如果在事务范围内成功完成,则如果事务是根事务,
11、那么当事务是根事务时就提交事务。如果范围内的不是根事务,那么会影响事务输出。 例2:使用TransactionScope实现隐式事务 void btnSave_Click(object sender, EventArgs e) try int categoryID; string connectionString = WebConfigurationManager.ConnectionStrings Mydatabase.ConnectionString; using (TransactionScope scope = new TransactionScope() using (SqlConn
12、ection connection = new SqlConnection(connectionString) categoryID = InsertCategory(connection); / 提交事务 scope.Complete(); lblResult.Text = Category is written successfully*Category ID= + categoryID.ToString(); catch (Exception ex) lblResult.Text = Exception is : + ex.Message; int InsertCategory(SqlC
13、onnection connection) Implicit Transactions using TransactionScope 在示例2中,对于Mydatabase数据库执行插入的SQL语句包括在使用using块的TransactionScope对象中。InsertCategroy()方法执行实际的向ProductCategory表插入新记录的工作。在插入记录后,该方法向调用者返回新近插入记录的标识值(类别ID列)。一旦代码成功执行,则调用TransactionScope对象的Complete()方法,以便告知.NET Framework语句已经成功执行完成,事务导致的结果将提交给数据库
14、。 以下是TransactionScope所完成的一些内容: 出现在using语句括号中的任何语句将在事务范围内执行。 任何在块中创建的连接将在事务中登记。 如果在using块中发生错误,则事务将自动回滚。 如果语句成功执行,那么作为工作的一部分,需要在事务中调用Complete()方法。 调用堆栈的每一步必须调用Complete(),以便提交事务。 TransactionScope对象无法了解是否应该提交或者放弃事务,TransactionScope的主要目标是避免开发人员与事务直接交互。为了解决这个问题,每个TransactionScope对象都有一个一致性位,其默认设置为false。通过
15、调用Complete()方法能够将一致性位设置为true。注意,只能调用一次Complete()。后续对Complete()的调用将引发InvalidOperation异常,因为在调用Complete()之后,不能保证还有事务性代码。 ASP.NET中的自动化事务 通过在ASP.NET页面中添加Transaction属性,可使得ASP.NET能够在系统中支持自动事务。利用Transaction属性,开发人员能够指示页面参与现有事务,开始新事务,或者不参与事务。下表列举了ASP.NET中可用的Transaction属性值。 通过在代码中的Page指令中设置Transaction属性能够定义页面支
16、持的事务级别。例如,插入以下指令能够保证页面活动总是在事务范围中执行: 如果省略Transaction属性,页面则禁用事务。使用System.EnterpriseServices.ContextUtil类的静态方法在ASP.NET页面中提交或者放弃事务。这些静态方法是SetComplete()和SetAbort()(它们分别对应Page事件CommitTransaction()和AbortTransaction())。以下代码列举了页面实现框架,该页面将Page指令的Transaction属性设置为Required,同时在CommitTransaction()和AbortTransaction
17、()事件中,编写处理事务结果所需的代码。void Page_Load(object sender, System.EventArgs e) AbortTransaction += new System.EventHandler(AbortTransactionEvent); CommitTransaction += new System.EventHandler(CommitTransactionEvent); try /* 在这里放置事务性代码 */ ContextUtil.SetComplete(); catch(Exception) ContextUtil.SetAbort(); voi
18、d AbortTransactionEvent(object sender,System.EventArgs e) /*用于回滚行为的代码*/ void CommitTransactionEvent(object sender,System.EventArgs e) /*用于提交行为的代码*/ 4. 何时使用事务 虽然.NET 2.0对事务提供了很好的支持,但是没有必要总是使用事务。使用事务的第一条规则是,在能够使用事务的时候都应该使用事务,但是不要使用过度。原因在于,每次使用事务,都会占用一定的开销。另外,事务可能会锁定一些表的行。还有一条规则是,只有当操作需要的时候才使用事务。例如,如果只
19、是从数据库中查询一些记录,或者执行单个查询,在大部分时候都不需要使用显式事务。 开发人员应该在头脑中始终保持一个概念,就是用于修改多个不同表数据的冗长事务会严重妨碍系统中的所有其他用户。这很可能导致一些性能问题。当实现一个事务时,遵循下面的实践经验能够达到可接受的结果:(1)避免使用在事务中的SELECT返回数据,除非语句依赖于返回数据;(2)如果使用SELECT语句,只选择需要的行,这样不会锁定过多的资源,而尽可能的提高性能;(3)尽量将事务全部写在T-SQL或者API中;(4)避免事务与多重独立的批处理工作结合,应该将这些批处理放置在单独的事务中;(5)尽可能避免大量更新。 另外,必须注意的一点就是事务的默认行为。在默认情况下,如果没有显式的提交事务,则事务会回滚。虽然默认行为允许事务的回滚,但是显式回滚方法总是一个良好的编程习惯。这不仅仅只是释放锁定数据,也将使得代码更容易读并且更少错误。 5. 小结 .NET 2.0提供的事务功能很强大,具体的内容远不止本文所讲解的这样简单。本文只是起到一个抛砖引玉的功能。希望读者能够灵活恰当的使用事务功能,而不要过去使用事务,否则可能会对性能起到消极的作用。
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1