您现在的位置是:网站首页> 编程资料编程资料

在ASP.NET 2.0中操作数据之六十一:在事务里对数据库修改进行封装_自学过程_

2023-05-24 305人已围观

简介 在ASP.NET 2.0中操作数据之六十一:在事务里对数据库修改进行封装_自学过程_

导言:

  正如我们在第16章《概述插入、更新和删除数据》里探讨的那样,GridView控件内建的功能支持对每行数据的编辑和删除功能,你只需要稍稍动一下鼠标就可以创建丰富的数据修改界面而不用写一行代码.但是,在某些情况下,这还不够,我们需要让用户能够成批地处理数据.

  比如,很多基于web(web-based)的电子邮件客户端,将所有邮件出来,每条邮件除了包含邮件信息(主题、发送者等)外,还包含一个checkbox控件。这些界面允许用户同时删除多个邮件,用户只需要选中邮件,再点"删除所选邮件"按钮.当用户要编辑多条不同的记录的时候,提供一个批编辑界面是比较理想的.我们用不着让用户每次都选中一条要编辑的记录,再做相关的修改,最后点“更新”按钮,在批编辑界面里每条记录都有各自的编辑选项,用户可以快速地编辑多条记录再点“Update All”按钮来保存对他们所做的修改.本系列我们将考察如何创建对数据进行添加、编辑、删除批处理的界面.

  如果想对批处理执行atomic operation(原子操作), 那么首先,所做的操作要么都执行成功要么都失败,另外还要对数据访问层进行扩充以支持database transactions(数据库事务)。数据库事务确保INSERT, UPDATE, 和 DELETE语句执行的atomicity(原子数)置于数据库事务的保护之下.另外,绝大多数的当代数据库系统都支持数据库事务.

  在本系列我们先看如何扩充数据访问层以支持数据库事务,接下来我们看如何创建页面以包含添加、更新、删除数据的批处理界面,让我们开始吧.

  注意:在批处理事务里修改数据时,原子数(atomicity)并非总数必要的。在批处理的某些情况下,某些修改成功某些修改失败是可以接受的。比如删除电子邮件时,有些邮件在删除过程中发生了数据库错误,有些邮件没有发生错误,对这种没有发生错误的邮件,批处理照样将其删除掉.对这种情况,我们没有必要设置数据访问层DAL支持数据库事务.不过在其它某些情况下,原子数是至关重要的.比如某个客户想把资金从一个银行帐户转移到另一个银行帐号,下面2个操作必须执行成功:首先,将第一个帐号的资金扣除,然后将资金转入第二个帐号.如果第一步执行成功,第二步执行失败,银行当然高兴,客户怕是要发疯了.在后面的文章里我们将创建添加、更新、删除的批处理界面,就算你不打算在这些页面里使用数据库事务,我也希望你照着本篇文章,对数据访问层进行扩展一支持数据库事务.

事务概述

绝大多数的数据库都支持事务,它可以将多个数据库命令当成一个逻辑单位进行处理.这些包含事务的命令要么都执行成功要么都执行失败.

一般来说,事务通过SQL命令来执行,使用如下的模式:

1.声明事务开始
2.执行构成事务的那些SQL命令
3.如果在第二步中的任何一个命令出错,执行事务回滚(rollback the transaction)
4.如果在第二步中的所有命令成功执行,提交事务

  这些SQL命令可以通过手写的方式输入,比如写SQL脚本、创建存储过程、也可以通过编程的方式来构建,比如使用ADO.NET技术或调用System.Transactions namespace命名空间的类.在本文,我们仅仅考察用ADO.NET技术管理事务.在后面的教程我们看如何在数据访问层Data Access Layer里使用存储过程,到那时,我们再来考察这些创建、回滚、提交事物的SQL命令。另外,要获得更多信息请参考文章《Managing Transactions in SQL Server Stored Procedures》(http://www.4guysfromrolla.com/webtech/080305-1.shtml

  注意:System.Transactions namespace命名空间的TransactionScope class类允许开发者通过编程的方式获取事务里的一系列命令,且允许事务包含多个数据源,甚至类型不同,比如:Microsoft SQL Server database, 或Oracle database,甚至Web service.本教程我们使用ADO.NET技术而非TransactionScope class类,是因为ADO.NET指定数据库事务更详细,且在很多情况下占用资源更少.此外,在某些情况下,TransactionScope class类要用到Microsoft Distributed Transaction Coordinator (MSDTC),围绕MSDTC的配置、执行和性能问题是比较专业、高级的问题稍微超出了本教程的范围.

  在ADO.NET里,通过调用SqlConnection class类的BeginTransaction method方法启动事务, 该方法返回一个SqlTransaction object对象.将构成事务的数据操作命令放在try...catch区域,如果在try区域的某个命令出错的话,程序将转到catch区域,在此,通过SqlTransaction object对象的Rollback method方法执行事务回滚。如果所有的命令执行成功,将调用位于try区域底部的SqlTransaction object对象的Commit method方法来提交事务.下面的代码片段揭示了该模式。要想看在ADO.NET里使用事务的更多例子,请参阅文章《Maintaining Database Consistency with Transactions》(http://aspnet.4guysfromrolla.com/articles/072705-1.aspx).

 // Create the SqlTransaction object SqlTransaction myTransaction = SqlConnectionObject.BeginTransaction(); try { /* * ... Perform the database transaction's data modification statements... */ // If we reach here, no errors, so commit the transaction myTransaction.Commit(); } catch { // If we reach here, there was an error, so rollback the transaction myTransaction.Rollback(); throw; } 

  默认情况下,强类型数据集(Typed DataSet)里的TableAdapters并不使用事务。为此,我们要对TableAdapter classes类进行扩展,以包含额外的方法以使用上述模式来执行事务。在第二步,我们看如何使用一个partial classes类来添加这些方法.

第一步:创建批处理数据的页面

  在我们考察如何扩展数据访问层DAL以支持数据库事务之前,让我们花点时间来创建一些ASP.NET web页面,我们在本章及后面三章将用到它们.

添加一个名为BatchData的新文件夹,再添加如下的 ASP.NET页面, 务必套用Site.master模板页.

Default.aspx
Transactions.aspx
BatchUpdate.aspx
BatchDelete.aspx
BatchInsert.aspx

//img.jbzj.com/file_images/article/201605/2016051809491921.gif
图1:添加相关的页面

就像其它文件夹里的Default.aspx页面一样,用SectionLevelTutorialListing.ascx用户控件来列出本部分的章节。将其从解决资源管理器里拖到Default.aspx页面.

//img.jbzj.com/file_images/article/201605/2016051809491922.gif
图2:将SectionLevelTutorialListing.ascx用户控件添加到Default.aspx页面

最后添加如下代码到Web.sitemap文件,具体的,将其添加到“Customizing the Site Map” 后面:

完成后,花几分钟在浏览器里登录页面,左面的菜单列出了本部分的各项

//img.jbzj.com/file_images/article/201605/2016051809492023.gif
图3:Site Map现在包含了本章节

第二步:更新数据访问层以支持数据库事务

  就像我们在第一章《创建一个数据访问层》探讨的一样,位于数据访问层的强类型数据集(Typed DataSet)由DataTables 和 TableAdapters构成.  DataTables保存数据,而TableAdapters提供相应的方法从数据库读取数据,并根据DataTables的改动对数据库做相应的更新,等等.记得TableAdapters有2种更新数据的模式——Batch Update 和 DB-Direct.就Batch Update模式而言, TableAdapter可以传入DataSet, DataTable, 或DataRows集,遍历这些数据对要添加、修改、删除的行执行相应的InsertCommand, UpdateCommand, or DeleteCommand方法。就DB-Direct模式而言,TableAdapter传入的是那些需要进行添加、更新、删除操作的某条记录的列的值,再使用这些值执行相关的InsertCommand, UpdateCommand, 或DeleteCommand命令.

  TableAdapter自动生成的方法并不使用事务.默认状态下,TableAdapter执行的每一个insert, update, 或delete操作都看作是单独的、互不相干的.假定在业务逻辑层BLL里使用DB-Direct模式来向数据库添加十条记录,代码将分十次调用TableAdapter的Insert方法. 如果前5条记录添加正常,而在添加第六条记录时发生异常,前5条记录仍然保存在数据库.同样的,用Batch Update模式来操作的话,效果亦然.

  在某些情况下,我们想确保在进行一系列的改动时引入原子数(atomicity).为此,我们必须手动扩展TableAdapter,通过添加一些新的方法将InsertCommand, UpdateCommand, 和DeleteCommands命令置于事务之下.在第一章《创建一个数据访问层》里,我们考察了使用部分类(partial classes)对强类型数据集(Typed DataSet)里的DataTable的函数进行扩充.该技术同样适用于TableAdapter.

  强类型数据集Northwind.xsd位于App_Code文件夹的DAL子文件夹里.在DAL文件夹里再创建一个名为TransactionSupport的子文件夹,再在里面添加一个新类,名为ProductsTableAdapter.TransactionSupport.cs (见图4).该类包含ProductsTableAdapter的使用事务的方法.

//img.jbzj.com/file_images/article/201605/2016051809492024.gif
图4:创建一个名为TransactionSupport的新文件夹并添加一个名为ProductsTableAdapter.TransactionSupport.cs的新类

在ProductsTableAdapter.TransactionSupport.cs文件里键入如下的代码:

 using System; using System.Data; using System.Data.SqlClient; using System.Configuration; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; namespace NorthwindTableAdapters { public partial class ProductsTableAdapter { private SqlTransaction _transaction; private SqlTransaction Transaction { get { return this._transaction; } set { this._transaction = value; } } public void BeginTransaction() { // Open the connection, if needed if (this.Connection.State != ConnectionState.Open) this.Connection.Open(); // Create the transaction and assign it to the Transaction property this.Transaction = this.Connection.BeginTransaction(); // Attach the transaction to the Adapters foreach (SqlCommand command in this.CommandCollection) { command.Transaction = this.Transaction; } this.Adapter.InsertCommand.Transaction = this.Transaction; this.Adapter.UpdateCommand.Transaction = this.Transaction; this.Adapter.DeleteCommand.Transaction = this.Transaction; } public void CommitTransaction() { // Commit the transaction this.Transaction.Commit(); // Close the connection this.Connection.Close(); } public void RollbackTransaction() { // Rollback the transaction this.Transaction.Rollback(); // Close the connection this.Connection.Close(); } } } 

  类声明里的关键字partial向编译器表明代码里添加的成员(members)是添加到命名空间NorthwindTableAdapters里的ProductsTableAdapter class类.我们注意到在文件的顶部有一个using System.Data.SqlClient声明,这是因为TableAdapter被设置为使用SqlClient provider,在其内部使用一个SqlDataAdapter object对象来向数据库发出命令.因此,我们需要使用SqlTransaction class类来启动事务,然后提交或回滚事务.如果没有使用Microsoft SQL Server数据库的话,你需要调用恰当的provider.

  这些方法被标记为public,我们可以在ProductsTableAdapter里,或数据访问层DAL的其它类,甚至是其它层比如业务逻辑层BLL来调用这些法.
BeginTransaction()方法打开了TableAdapter的内部的SqlConnection(如果需要的话), 开启事务并赋值给Transaction属性,并将事务分配(attache)给SqlDataAdapter的SqlCommand objects对象.CommitTransaction()和 RollbackTransaction()方法在关闭内部的Connection object对象前分别调用Transaction object对象的Commit 和 Rollback方法.

  添加上述代码后,我们将在ProductsDataTable 或业务逻辑层BLL里添加方法以执行一系列的置于事务之下的命令. 下面的代码在Batch Update pattern模式里使用一个事务来更新一个ProductsDataTable instance实例.它调用BeginTransaction method方法来启动一个事务,然后用一个try...catch模块来发布数据更改命令.如果调用Adapter object对象的Update方法出现异常,那么将转到catch区域,对事务进行回滚.记得执行Batch Update pattern模式的Update方法将遍历ProductsDataTable里的所有行(rows),执行相应的InsertCommand, UpdateCommand, 和DeleteCommands命令.如果这些命令中的其中一个出现异常,事务将回滚,撤销在事务里的所做的更改.如果Update命令全部执行无异常,那么提交事务.

 public int UpdateWithTransaction(Northwind.ProductsDataTable dataTable) { this.BeginTransaction(); try { // Perform the update on the DataTable int returnValue = this.Adapter.Update(dataTable); // If we reach here, no errors, so commit the transaction this.CommitTransaction(); return returnValue; } catch { // If we
                
                

-六神源码网