在ASP.NET 2.0中操作数据之四十八:对SqlDataSource控
本文主要是在ASP.NET 2.0中,SqlDataSource控件如何通过配合SQL语句实现开放式并发控制。在更新和删除操作中,通过扩展WHERE子句,SqlDataSource控件能够应对大多数并发问题。
导言部分简要回顾了之前关于SqlDataSource控件的教程内容,并引出本文的主题——开放式并发控制。在ASP.NET开发中,当多个用户同时编辑或删除相同的数据时,就可能发生数据覆盖的问题。这就需要我们实施并发控制,其中开放式并发控制是其中一种策略。
关于开放式并发控制的具体实施,我们可以借助SqlDataSource控件的UPDATE和DELETE命令来实现。在这个过程中,当用户在可编辑的GridView中点击编辑按钮时,记录的原始值会从数据库中读取出来并显示在界面上。用户在修改完毕后点击更新按钮,此时会将原始值和新修改的值一起发送到业务逻辑层,然后再到数据访问层。数据访问层会发出一个SQL语句,这个语句只会更新那些开始编辑时的原始值与数据库中当前值一致的记录。这就是开放式并发控制的基本原理。
在“高级SQL生成选项”对话框中(如图1所示),我们可以选择使用开放式并发控制选项。当这个选项被选中时,只有在自上一次成功保存以来数据没有发生任何改变的情况下,才能成功执行更新或删除操作。这样可以有效地避免数据覆盖的问题。
以一个具体的例子来说明,假设有两个用户Jisun和Sam同时编辑一条产品记录。Jisun首先修改了产品名称并点击了更新按钮,此时数据库中的记录被更新。然后Sam也开始修改这条记录,但他并不知道Jisun已经修改过。如果Sam的修改覆盖了Jisun的更改,就会产生数据不一致的问题。但是如果我们使用了开放式并发控制,那么只有当Sam开始编辑时的记录值与数据库中的值一致时,他的修改才会被接受。否则,系统会提示他,他的更改不能保存,因为别的用户已经修改了同一条记录。
掌控并发更新:SqlDataSource控件与乐观并发控制策略
在数据库应用中,为了确保数据的一致性和完整性,处理并发更新是一个重要的环节。为了实现成功的更新或删除操作,原始值必须与数据库中相应的值保持一致。这涉及到一种被称为乐观并发控制的策略。SqlDataSource控件正是通过扩展WHERE子句,将原始值用于比较,从而实现了这一策略。
以图3中的例子为例,假设我们正在进行狼蚁网站的SEO优化工作。在编辑产品的名称和价格时,只有当数据库中的当前值与GridView中开始编辑时的原始值相匹配时,UPDATE语句才会执行。用户输入的新值通过@ProductName和@UnitPrice参数传递,而最初点击编辑按钮时加载到GridView中的值则通过@original_ProductName和@original_UnitPrice参数保存。
在SqlDataSource控件的支持下,实现乐观并发控制变得相对简单。以OptimisticConcurrency.aspx页面为例,我们首先需要创建一个SqlDataSource控件,并将其ID设置为ProductsDataSourceWithOptimisticConcurrency。在智能标签中,我们设置数据源为“NORTHWINDConnectionString”数据库。此页面将包含一个GridView控件,用于编辑表Products中的数据。在配置选择语句时,我们选择返回ProductID、ProductName、UnitPrice和Discontinued列。然后,通过高级设置选项启用生成INSERT、UPDATE和DELETE语句,并选择使用乐观并发控制策略。完成这些设置后,我们就可以进行后续的数据编辑操作了。
完成数据源设置向导后,别忘了检查DeleteCommand和UpdateCommand属性,以及DeleteParameters和UpdateParameters标签。这些属性和标签对于确保并发更新的成功执行至关重要。通过深入了解这些设置,我们可以更好地理解SqlDataSource控件是如何实现乐观并发控制的。这将有助于我们在开发数据库应用程序时更好地处理并发更新问题,从而提高系统的稳定性和性能。
通过SqlDataSource控件和乐观并发控制策略的结合使用,我们可以轻松地在数据库应用程序中实现成功的更新和删除操作。这不仅提高了数据的安全性,还增强了用户体验。在Web开发中,数据的更新和删除操作是常见的功能需求。当你选择使用乐观并发模式时,为了确保数据的完整性和一致性,需要对数据库命令进行细致的调整。让我们深入一下这些操作背后的代码和逻辑。
最快的方法是直接查看页面源代码,进入“源模式”,你将看到UpdateCommand的值类似于下面的样子:
UPDATE [Products] SET
[ProductName] = @ProductName,
[UnitPrice] = @UnitPrice,
[Discontinued] = @Discontinued
WHERE
[ProductID] = @original_ProductID AND
[ProductName] = @original_ProductName AND
[UnitPrice] = @original_UnitPrice AND
[Discontinued] = @original_Discontinued
这是一个典型的SQL更新语句,用于修改数据库中的产品信息。在
同样的逻辑也应用在DeleteCommand中。删除操作基于原始的产品ID、名称、单价和停用状态来定位要删除的记录。这确保了只有匹配这些条件的记录会被删除。
在选择“Use optimistic concurrency”选项后,对UpdateCommand和DeleteCommand的WHERE子句进行了扩展,增加了参数化的原始值比较。对SqlDataSource控件的两个属性进行了调整:ConflictDetection和OldValuesParameterFormatString。这些调整确保了在进行更新或删除操作时,会传递原始值以进行比较,从而避免潜在的并发冲突。这种并发控制策略允许多个用户同时访问数据,但只在数据未被其他用户修改的情况下允许更新或删除操作。如果数据已被其他用户修改并提交了更改,则当前用户的操作将失败,并可能需要重新加载数据或进行其他处理。这是一种确保数据完整性和一致性的重要机制。当SqlDataSource的ConflictDetection属性设置为“CompareAllValues”时,对数据冲突的解决有了更精细的控制。此刻,每一个原始数据值都会被添加到命令中,以便进行详细的比对。OldValuesParameterFormatString属性则为这些原始值设定了命名规则,向导会以“original_{0}”的形式,为UpdateCommand和DeleteCommand中的原始数据值以及UpdateParameters和DeleteParameters中的参数进行命名。
接下来要的是如何处理NULL值的问题。在使用开放式并发时,那些由数据源向导自动生成的UPDATE和DELETE命令,在面临含有NULL值的记录时会出现处理难题。这是因为当表中含有NULL值时,传统的WHERE字句无法正确识别和处理这些记录。
以SqlDataSource的UpdateCommand语句为例:
UPDATE [Products] SET
[ProductName] = @ProductName,
[UnitPrice] = @UnitPrice,
[Discontinued] = @Discontinued
WHERE
[ProductID] = @original_ProductID AND
[ProductName] = @original_ProductName AND
[UnitPrice] = @original_UnitPrice AND
[Discontinued] = @original_Discontinued
如果表Products中的UnitPrice列的值允许为NULL,那么WHERE字句中的“[UnitPrice] = @original_UnitPrice”将无法正确识别那些UnitPrice为NULL的记录,因为NULL = NULL的结果是False。这就导致了含有NULL值的记录无法被编辑或删除。
这个漏洞最早在2004年6月就被报告给微软。据业内消息,微软将在ASP.NET的下一个版本中进行修复。在我们等待微软修复的我们也可以采取手动修改的方式来解决这个问题。
我们需要在UpdateCommand和DeleteCommand属性中,针对所有允许为NULL的列进行手动修改。具体来说,就是将“[ColumnName] = @original_ColumnName”修改为:
(
([ColumnName] IS NULL AND @original_ColumnName IS NULL)
OR
([ColumnName] = @original_ColumnName)
)
在更新产品信息的数据库操作中,我们针对[Products]表进行了一系列的更新操作。针对每个产品,我们都会对其名称(ProductName)、单位价格(UnitPrice)以及停产状态(Discontinued)进行细致的调整。在此过程中,我们需要明确指定哪些产品需要进行更新,确保我们的操作精确无误。我们的更新条件包括产品ID(ProductID)、原始产品名称(original_ProductName)以及原始单位价格(original_UnitPrice)。只有在产品的原始单位价格与数据库中一致时,我们的更新操作才会被执行。而当某些产品满足这些条件时,我们也会从数据库中彻底删除它们。这样的操作确保了数据的准确性和实时性。
在Web应用程序中,为了实现对产品的编辑和删除操作,我们需要在页面上添加一个功能强大的GridView控件。当我们的SqlDataSource控件支持开放式并发时,GridView控件将发挥巨大的作用。通过拖放一个GridView到页面上,我们将其ID设置为Products,并将其绑定到先前添加的SqlDataSource控件ProductsDataSourceWithOptimisticConcurrency。通过这个控件,我们可以轻松启用GridView的编辑和删除功能。
经过优化后的GridView界面更加友好和直观。我们移除了ProductID列,将ProductName列的标题设置为“Product”,并将UnitPrice列的标题设置为“Price”。为了确保数据的准确性和完整性,我们为ProductName添加了必要的验证控件,确保输入的产品名称是有效的;为UnitPrice添加了比较验证控件,确保输入的价格是格式化的数字值。
值得注意的是,为了确保GridView控件的正常运行,我们必须激活其视图状态(ViewState)。因为当GridView控件传递原始值时,它会将这些值保存在视图状态中。这样,即使在页面刷新或重新加载时,这些原始值仍然能够被保留和识别,从而确保我们的编辑和删除操作能够顺利进行。通过这种方式,我们为用户提供了一个流畅、高效且安全的产品编辑和删除体验。经过优化和调整,GridView控件与SqlDataSource控件的联合声明代码,仿佛与狼蚁网站的SEO策略相呼应。以下是修改后的代码展示:
```asp
... ConnectionString="..." ...
DeleteCommand="DELETE FROM [Products]...";
OldValuesParameterFormatString="original_{0}";
SelectCommand="SELECT ...";
UpdateCommand="UPDATE [Products]...";
...
...
```
在实际应用中,体验开放式并发控制所带来的优势是一种全新的感受。想象一下,你正在处理一个高流量的在线购物平台,用户们同时在线浏览、点击购买、评价商品。GridView控件与SqlDataSource控件的优化合作就像是网站的流畅交响乐,确保用户即使在多用户并发操作的情况下也能享受到快速响应和无缝的数据交互体验。当你看到GridView中的商品数据在用户间无缝地更新、删除时,不禁感叹开放式并发控制的巧妙之处。它为你的网站提供了一个稳健的数据处理机制,确保用户操作的顺利进行,提升了整个网站的效率和用户体验。这种体验让人感受到技术与实际业务需求的完美结合。在同时打开的两个浏览器里访问OptimisticConcurrency.aspx页面,两条几乎相同的操作路径展现眼前。在第一个浏览器里,用户打开了产品列表的第一条记录并点击编辑按钮,对产品名称进行了修改并提交了更改。在浏览器发生回传之后,GridView控件重回“预编辑”状态,显示着新的产品名称。
与此在第二个浏览器里,用户也打开了相同的产品记录并尝试修改其价格,但并未更改产品名称。提交编辑后,浏览器同样经历了回传过程,GridView控件再次回到“预编辑”状态。这次呈现的产品信息却与第一个浏览器中的不同——产品的名称已经更新,但价格修改却未能生效。这一切都在静默中发生,似乎没有任何迹象表明刚才发生了并发冲突。
这种现象背后的原因,源于UPDATE命令中的WHERE子句。这个看似普通的UPDATE语句实际上在执行时过滤掉了所有记录,因为没有一条记录能满足所有条件。我们具体来看这个UPDATE语句:
```sql
UPDATE [Products] SET
[ProductName] = @ProductName,
[UnitPrice] = @UnitPrice,
[Discontinued] = @Discontinued
WHERE
[ProductID] = @original_ProductID AND
[ProductName] = @original_ProductName AND
(([UnitPrice] IS NULL AND @original_UnitPrice IS NULL) OR ([UnitPrice] = @original_UnitPrice)) AND
[Discontinued] = @original_Discontinued
```
当第二个浏览器尝试更新记录时,WHERE子句中的原始产品名(例如“Chai”)与当前任何一条记录的产品名不匹配(因为第一个浏览器已经更改了产品名)。“[ProductName] = @original_ProductName”这个条件将返回False,导致更新操作失败。
值得注意的是,删除操作的原理也是相同的。在同时打开的两个浏览器中,如果一个浏览器先对某产品进行了更改,另一个浏览器再尝试删除该产品,同样会因为原始值与更新后的值不匹配而导致删除失败。
对于最终用户来说,他们可能会遇到一种情况:在点击“更新”按钮后,GridView控件返回“预编辑”状态,但他们的更改却无声无息地消失了。没有任何直观的提示告诉他们更新已经失败。当用户的更新因并发冲突而失败时,我们应该有一种机制来提醒用户。例如,我们可以让GridView控件保持在“编辑”状态,或者在页面上添加一个Label控件来显示提醒信息。
创建处理器以响应RowUpdated和RowDeleted事件
在响应数据更新的过程中,我们通常需要处理各种可能出现的情况,其中之一就是并发冲突。针对这种情况,我们为Products控件的RowUpdated和RowDeleted事件创建了处理器。以下是相关的代码:
在RowUpdated事件处理器中:
```csharp
protected void Products_RowUpdated(object sender, GridViewUpdatedEventArgs e)
{
if (e.AffectedRows == 0) // 如果未影响任何行,说明发生了并发冲突
{
ConcurrencyViolationMessage.Visible = true; // 显示并发冲突的提示信息
e.KeepInEditMode = true; // 保持GridView在编辑状态以显示的更改尝试
Products.DataBind(); // 重新绑定数据以更新GridView显示的内容
}
}
```
在RowDeleted事件处理器中:
```csharp
protected void Products_RowDeleted(object sender, GridViewDeletedEventArgs e)
{
if (e.AffectedRows == 0) // 如果未删除任何行,同样说明发生了并发冲突
ConcurrencyViolationMessage.Visible = true; // 同样显示并发冲突的提示信息
}
```
每当发生RowUpdated或RowDeleted事件时,我们都会检查`e.AffectedRows`属性是否为0。如果为0,则表示在处理更新或删除操作时发生了并发冲突。在这种情况下,我们会设置ConcurrencyViolationMessage这个Label控件的Visible属性为true,以显示并发冲突的提示信息。特别地,在RowUpdated事件处理器中,我们还会将GridView控件的KeepInEditMode属性设置为true,使其保持在编辑状态并显示的更改尝试。然后我们通过调用GridView的DataBind()方法来更新显示内容。如图9所示,当用户遇到并发冲突时,将显示相应的提示信息。
图9:并发冲突时的提示信息展示
在开发多人可编辑相同数据的应用程序时,并发冲突是一个需要考虑的问题。ASP.NET数据Web控件和数据源控件默认情况下并不提供并发控制。通过扩展UPDATE和DELETE语句的WHERE子句,我们可以使用SqlDataSource来应对大部分并发情况。尽管如此,我们仍需注意处理包含NULL值列的情况,正如在之前的教程中的那样,这里仍存在一些潜在的漏洞。本章是对SqlDataSource的深入的结束,接下来的教程将继续层次结构以及使用ObjectDataSource处理数据。祝编程愉快!
作者简介:本系列教程由Scott Mitchell撰写。Scott Mitchell是ASP/ASP.NET领域的资深专家,著有六本相关书籍,同时也是4GuysFromRolla.的创始人。自1998年以来,他一直致力于应用微软Web技术。可以查看他撰写的全部教程《XXX》,这些教程对ASP.NET的学习者会有很大的帮助。
编程语言
- 在ASP.NET 2.0中操作数据之四十八:对SqlDataSource控
- 如何采集静态文章系统
- 保存远程图片函数修改正版
- 原生JS 购物车及购物页面的cookie使用方法
- Win7 安装 Mysql 5.6的教程图解
- php采集内容中带有图片地址的远程图片并保存的
- PHP使用SMTP邮件服务器发送邮件示例
- 基于jQuery实现Ajax验证用户名是否存在实例
- MVC+EasyUI+三层新闻网站建立 详情页面制作方法(
- vue父组件通过props如何向子组件传递方法详解
- js带前后翻页的图片切换效果代码分享
- vue element 生成无线级左侧菜单的实现代码
- jQuery+css3实现文字跟随鼠标的上下抖动
- laravel-admin的图片删除实例
- 前端从浏览器的渲染到性能优化
- php操作access数据库的方法详解