在ASP.NET 2.0中操作数据之六十一:在事务里对数据

网络编程 2025-04-05 14:35www.168986.cn编程入门

事务在数据处理中扮演着至关重要的角色,它确保了数据的完整性和一致性。在ASP.NET 2.0中,使用事务能够确保对数据的修改要么全部成功,要么全部失败,从而避免了数据在修改过程中的不确定性。

导言

在之前的章节中,我们了GridView控件内建的功能,支持对每行数据的编辑和删除。通过简单的鼠标操作,我们可以创建丰富的数据修改界面,而无需编写大量代码。在某些场景下,我们需要让用户能够批量处理数据,例如基于Web的电子邮件客户端。

大多数现代数据库系统都支持数据库事务。在本系列文章中,我们将首先如何扩展数据访问层以支持数据库事务。接下来,我们将创建页面,包含添加、更新和删除数据的批处理界面。

值得注意的是,在批处理事务中修改数据时,并非所有情况都需要原子性。在某些场景下,如删除电子邮件时,某些操作失败是可以接受的。但对于其他场景,如银行转账等关键操作,原子性至关重要。如果转账的一部分成功而另一部分失败,可能会导致客户资金的不一致。

事务概述

绝大多数数据库都支持事务,它可以将多个数据库命令作为一个逻辑单位进行处理。这些包含事务的命令要么全部执行成功,要么全部执行失败。事务通过SQL命令执行,包括以下几个步骤:

1. 声明事务开始。

2. 执行构成事务的那些SQL命令。

3. 如果第二步中的任何命令出错,执行事务回滚。

4. 如果第二步中的所有命令成功执行,则提交事务。

我们将研究如何使用ADO.NET技术来管理事务。在后续的教程中,我们将如何在数据访问层中使用存储过程,并进一步研究创建、回滚和提交事务的SQL命令。通过扩展数据访问层以支持数据库事务,我们可以为用户创建更强大、更稳健的数据处理体验。掌握 SQL Server 中的事务管理对于数据库应用程序至关重要,特别是当我们处理关键业务数据时。在 ADO.NET 中,通过 SqlConnection 类提供的机制,我们可以轻松管理数据库事务。本文将深入如何使用 SqlConnection 类中的 BeginTransaction 方法来启动事务,并展示如何在 ASP.NET 应用程序中实现这一过程。

我们要理解 System.Transactions 命名空间中的 TransactionScope 类为开发者提供了强大的事务管理能力。它可以跨多个数据源甚至不同类型(如 SQL Server、Oracle 数据库或 Web 服务)来管理事务。对于本教程,我们将专注于使用 ADO.NET 来处理数据库事务,因为它针对特定数据库提供了更详细的控制,并且在许多情况下资源占用更少。

在 ADO.NET 中,启动事务非常简单。我们通过 SqlConnection 对象调用 BeginTransaction 方法来创建一个新的 SqlTransaction 对象。这个对象代表了一个事务,我们可以在其上执行一系列数据库操作。这些操作被封装在一个 try-catch 块中,以确保在发生错误时能够回滚事务,或者在所有操作都成功完成时提交事务。

下面是一个简单的代码示例,展示了如何在 ADO.NET 中使用事务:

```csharp

// 创建 SqlConnection 对象并连接到数据库

using (SqlConnection connection = new SqlConnection(connectionString))

{

connection.Open();

// 创建 SqlTransaction 对象

SqlTransaction transaction = connection.BeginTransaction();

try

{

// 使用 transaction 进行数据库操作

// 例如,执行一些 SQL 命令或调用存储过程等

// 如果所有操作都成功,则提交事务

transactionmit();

}

catch (Exception ex)

{

// 如果发生错误,则回滚事务

transaction.Rollback();

// 重新抛出异常以供上层处理

throw;

}

} // 连接和事务在此处自动关闭,因为使用了 using 语句块

```

- Default.aspx:本部分的默认页面,使用 SectionLevelTutorialListing.ascx 用户控件列出章节。

- Transactions.aspx:介绍事务处理的页面。

- BatchUpdate.aspx:处理批量更新的页面。

- BatchDelete.aspx:处理批量删除的页面。

将资源管理器与Default.aspx页面融合

让我们开始一项新的旅程,将资源管理器中的元素整合到Default.aspx页面。这不仅是对技术的挑战,也是对创新和用户体验的挑战。让我们一起通过操作界面将强大的功能带入到Web应用程序中。

第一步是将名为SectionLevelTutorialListing.ascx的用户控件添加到Default.aspx页面。这听起来有些复杂,但一旦你掌握了它,你会发现它实际上是非常直观和有趣的。将这个控件从资源管理器拖放到Default.aspx页面上,你会看到新的功能立即呈现在你的眼前。

接下来,我们需要更新网站的导航结构。在Web.sitemap文件中添加新的节点信息,这些节点包括处理批处理数据的相关页面。具体地,在“Customizing the Site Map”节点之后添加以下内容:

节点信息关于批处理数据操作

“Working with Batched Data”:了解如何进行批处理操作,而不是逐行操作。指向的页面是~/BatchData/Default.aspx。

“Adding Support for Transactions”:了解如何扩展数据访问层以支持数据库事务。对应的页面是~/BatchData/Transactions.aspx。

“Batch Updating”:构建一个批处理更新界面,GridView中的每一行都可以编辑。对应的页面是~/BatchData/BatchUpdate.aspx。

“Batch Deleting”:创建一个批处理删除界面,通过为GridView的每一行添加复选框来实现。对应的页面是~/BatchData/BatchDelete.aspx。

当你完成这些步骤后,只需在浏览器中登录页面,左侧菜单就会列出这些新增的功能模块。这不仅是技术上的进步,更是用户体验的提升。

接下来,我们进入第二章的——更新数据访问层以支持数据库事务。正如我们在前章已经过的那样,数据访问层中的强类型数据集是由DataTables和TableAdapters构成的。DataTables用于存储数据,而TableAdapters则提供方法来从数据库读取数据并根据DataTables的变化进行相应的数据库更新操作。值得注意的是,TableAdapters有两种数据更新模式——Batch Update和DB-Direct。对于Batch Update模式而言,TableAdapter可以接收DataSet、DataTable或DataRows集合,遍历这些数据并对要添加、修改或删除的行执行相应的InsertCommand、UpdateCommand或DeleteCommand方法。通过这种方式,我们可以将复杂的数据库操作变得更加直观和高效。

以上就是关于如何将资源管理器中的元素整合到Web应用程序的详细步骤和介绍。通过这种方式,我们可以为网站增添更多功能,提升用户体验,同时也展示了技术的无限可能性和魅力。关于DB-Direct模式中的TableAdapter,它主要处理数据的添加、更新和删除操作。具体来说,TableAdapter接收需要修改的某条记录的列值,然后使用这些值执行InsertCommand、UpdateCommand或DeleteCommand命令。

在默认状态下,TableAdapter执行的每一个insert、update或delete操作都是独立的,彼此之间没有直接的联系。这就意味着如果在业务逻辑层BLL使用DB-Direct模式向数据库添加数据时发生异常,已经添加的数据不会被撤销。同样的,如果使用Batch Update模式,效果也是一致的。

在某些场景下,我们可能需要确保一系列的数据修改操作具有原子性,即要么全部成功,要么全部失败。为此,我们需要对TableAdapter进行扩展,使其支持事务处理。

以Northwind强类型数据集为例,我们可以在DAL(数据访问层)的子文件夹中创建一个名为TransactionSupport的新文件夹。然后,在这个文件夹中添加一个名为ProductsTableAdapter.TransactionSupport.cs的新类。这个类的主要作用是提供使用事务的方法。

在ProductsTableAdapter.TransactionSupport.cs文件中,我们需要使用System.Data.SqlClient命名空间下的SqlTransaction类来管理事务。这个类提供了BeginTransaction、CommitTransaction和RollbackTransaction等方法,分别用于开启事务、提交事务和回滚事务。

在ProductsTableAdapter类中,我们需要添加BeginTransaction、CommitTransaction和RollbackTransaction等方法。这些方法的主要作用是管理SqlTransaction对象,确保我们的数据修改操作在一个事务中完成。

具体来说,我们在BeginTransaction方法中开启事务,并将事务分配给TableAdapter中的命令。然后,在CommitTransaction方法中提交事务,而在RollbackTransaction方法中回滚事务。

接下来,我们可以在ProductsDataTable或业务逻辑层BLL中添加一个名为UpdateWithTransaction的方法。这个方法的主要作用是使用事务来更新数据。它首先调用BeginTransaction方法开启事务,然后尝试执行数据表更新操作。如果更新操作成功,则提交事务;如果发生异常,则回滚事务。

通过扩展TableAdapter以支持事务处理,我们可以确保一系列的数据修改操作具有原子性,提高数据的完整性和可靠性。以上述的UpdateWithTransaction方法为例,它确保了数据更新的原子性,即在更新过程中如果出现任何错误,所有的更改都会被撤销,从而保证了数据的完整性。深化理解:事务处理在业务逻辑层中的灵活应用

在数据管理的世界里,事务处理扮演着至关重要的角色,它确保了数据操作的完整性和一致性。特别是在涉及到复杂业务逻辑的应用程序中,如何巧妙地将事务处理融入业务逻辑层,成为了一个值得深入的话题。

考虑到一个业务逻辑层中的ProductsBLL类,我们可以为其增加事务处理功能,使得数据操作更为稳健。已经存在的ProductsTableAdapter的name属性为Adapter,我们可以利用其来实施事务管理。具体的DeleteProductsWithTransaction方法已经展现了一个很好的示例。它接受一个包含ProductID的列表,并对每一个ID执行删除操作,确保了操作的原子性。

方法细节再探:

当谈到在多个TableAdapters中应用事务时,我们面临的是更为复杂的场景。假设我们需要删除一个类别,并且在删除之前要重新分配该类别下的产品到其他类别。这涉及到两个数据库表:Products表和Categories表。

这时,我们不能仅仅依赖单一的TableAdapter来处理这种跨表的业务逻辑。一个解决方案是,在CategoriesTableAdapter中新增一个方法DeleteCategoryAndReassignProducts,并为其设计一个存储过程。这个存储过程会在事务中完成产品的重新分配和类别的删除。这种方法的好处是它将复杂的业务逻辑与数据库操作紧密结合,保证了数据的一致性。

进一步的优化与创新:

除了上述方法,我们还可以考虑在数据访问层中添加一个新的类,这个类专门用于处理涉及多个TableAdapters的事务。在这个类中,我们可以创建CategoriesTableAdapter和ProductsTableAdapter的实例,并确保这两个TableAdapters使用相同的SqlConnection实例。通过这种方式,我们可以在同一事务中调用多个TableAdapters的方法,实现跨表的业务逻辑处理。这种方法更为灵活,尤其是在处理复杂的业务场景时,它提供了更高的代码组织性和可维护性。

在数据交互的海洋中,事务扮演着至关重要的角色。为了深入理解并实现其重要性,让我们逐步如何向业务逻辑层添加带有事务处理功能的方法。在这个过程中,我们将着重关注一个名为UpdateWithTransaction的方法,它将在业务逻辑层的核心部分发挥重要作用。

第一步是向数据访问层(DAL)的ProductsTableAdapter添加UpdateWithTransaction方法。这一步骤的实现意味着每次对数据库进行更新操作时,都会启动一个事务。在事务的包裹下,我们执行分配products和删除category的操作。如果在执行过程中遇到任何问题,事务会进行回滚,确保数据的完整性和一致性。

紧接着,第二步是向业务逻辑层(BLL)添加UpdateWithTransaction方法。虽然表现层可以直接调用DAL中的UpdateWithTransaction方法,但为了代码的清晰和组织性,我们选择在业务逻辑层实现这个方法。在ProductsBLL类中,我们添加了一个名为UpdateWithTransaction的方法,它简单调用对应的DAL方法。现在,ProductsBLL类拥有两个关键方法:UpdateWithTransaction和DeleteProductsWithTransaction。

DeleteProductsWithTransaction方法是一个典型的事务处理示例。它首先启动一个事务,然后尝试删除列表中的每个产品。如果出现任何错误,事务将回滚,确保数据的完整性不受影响。这种设计对于确保批量操作的原子性非常关键。

值得注意的是,与ProductsBLL类中的其他方法不同,上述新方法并未包含DataObjectMethodAttribute属性。这是因为我们将直接在ASP.NET页面的后台代码中调用这些方法。DataObjectMethodAttribute属性通常用于指示哪些方法应出现在ObjectDataSource控件的设置数据源向导的特定标签(如SELECT、UPDATE、INSERT或DELETE)中。由于GridView控件本身不支持“批编辑”或“批删除”功能,我们将通过编程方式调用这些方法来实现这些功能。

图5:配置ObjectDataSource以使用ProductsBLL Class的GetProducts方法

图6:在UPDATE, INSERT, 和DELETE标签里选择“(None)”

在Visual Studio中完成这些设置后,GridView控件将自动添加BoundFields以及一个CheckBoxField。除了ProductID、ProductName、CategoryID和CategoryName等列,其他列都会被删除。ProductName和CategoryName列的HeaderText属性会被重命名为“Product”和“Category”。为了增强用户体验,我们还将启用分页功能。完成这些修改后,GridView和ObjectDataSource控件的代码看起来将与经过狼蚁网站SEO优化的代码相似。

以下是GridView和ObjectDataSource的声明代码示例:

```aspx

AutoGenerateColumns="False" DataKeyNames="ProductID"

DataSourceID="ProductsDataSource">

InsertVisible="False" ReadOnly="True" SortExpression="ProductID" />

OldValuesParameterFormatString="original_{0}" SelectMethod="GetProducts" TypeName="ProductsBLL">

```

接下来,为GridView控件添加三个按钮Web控件。第一个按钮的文本为“Refresh Grid”,第二个为“Modify Categories (WITH TRANSACTION)”,第三个为“Modify Categories (WITHOUT TRANSACTION)”。

```aspx

```

图7:页面包含一个GridView控件和三个按钮Web控件

为这三个按钮的Click事件创建事件处理器。例如:

Refresh按钮的Click事件处理器仅仅调用Products GridView的DataBind方法,重新绑定数据到GridView控件。

第二个事件处理器会更新products的CategoryID属性并调用BLL层中的事务处理方法执行数据库更新。这里存在一个潜在问题,即随着ProductID值的增大,直接将此值赋给CategoryID可能会导致数据不一致,因为Category表里的种类数量是有限的。

第三个事件处理器与第二个类似,但不使用事务处理进行更新操作。无论哪种方式,更新完成后都会刷新GridView以显示的数据。

在第三个事件处理过程中,我们将ProductID的值分配给CategoryID属性,并通过ProductsTableAdapter的默认Update方法更新数据库。这个Update方法在执行命令时并没有采用事务处理,所以只要没有违反外键约束的更新都会成功执行。

为了验证这一过程,我们在浏览器中登录相关页面。初始展示的是如图8所示的Products分页GridView控件。点击“Modify Categories (WITH TRANSACTION)”后,页面回传并尝试更新所有产品的CategoryID值。这一操作会导致外键约束的违反(如图9所示)。

图8:Products分页显示

图9:外键约束违反提示

当点击浏览器的Back按钮,再点“Refresh Grid”按钮时,界面恢复到和图8一样的状态。这是因为发生了外键约束违反,导致操作回滚。

接下来,点击“Modify Categories (WITHOUT TRANSACTION)”按钮,同样会触发外键约束的违反(如图9)。但这次,对于那些赋予了有效值的CategoryID操作并不会回滚。点击浏览器的Back按钮,再点“Refresh Grid”按钮后,如图10所示,最初8个产品的CategoryID值已经发生改变。例如,Chang的CategoryID值从1变更为2。

图10:部分Product的CategoryID值已更改

在默认情况下,TableAdapter的方法并未使用事务处理数据库命令。但通过额外的工作,我们可以在ProductsTableAdapter class中添加用于创建、提交、回滚事务的方法。本教程中,我们创建了BeginTransaction, CommitTransaction,和RollbackTransaction这三个方法。我们学会了如何在try...catch模块中使用这些方法,执行一系列的修改命令。具体来说,我们在ProductsTableAdapter中创建了UpdateWithTransaction方法,它采用Batch Update模式对ProductsDataTable中的每行记录执行必要的更改。在BLL的ProductsBLL class中添加了DeleteProductsWithTransaction方法,它以一系列ProductID为输入参数,采用DB-Direct模式删除每个产品。这些方法在开始时创建一个事务,然后在try...catch模块中执行数据更改命令。如果出现异常,则回滚事务;否则,提交事务。

第五步展示了事务的作用。在接下来的三章,我们将基于本章的内容,创建批更新、批删除、批添加的用户界面。让我们享受编程的乐趣吧!

关于作者:

本文作者Scott Mitchell是ASP/ASP.NET领域的专家,著有六本相关书籍。作为4GuysFromRolla.的创始人,他从1998年开始应用微软Web技术。可以查看他全部教程《XXX》,希望对大家的ASP.NET学习有所帮助。

注:文章中提到的内容、图示和链接等应与实际情况相符,以确保读者能够理解和跟随。如有需要,还可以添加更多细节和实例来增强文章的生动性和吸引力。

上一篇:setTimeout时间设置为0详细解析 下一篇:没有了

Copyright © 2016-2025 www.168986.cn 狼蚁网络 版权所有 Power by