科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件DataSet中的数据并发性异常

DataSet中的数据并发性异常

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

本文调查了数据并发性异常背后的通常起因,介绍了解决这些问题的技术

作者:陶刚编译 来源:yesky 2007年11月9日

关键字:

  • 评论
  • 分享微博
  • 分享邮件
示例程序

  一个虚拟的公司使用该顾客服务应用程序建立顾客订单,更新顾客的个人信息。有很多客户销售代表(CSR)在桌面上使用该应用程序。CSR使用电话获取订单,从顾客那儿收集个人信息和支付信息。顾客记录保存在数据库中以提高回头客处理订单的速度,CSR接着建立一个订单并把产品项添加上去,指定数量和目前的价格,所有的信息收集后,CSR点击Place Order按钮,向数据库中插入顾客和订单记录。

  CSR也使用应用程序执行通过电子或者缓慢的邮件发送给公司的请求。这些请求在CSR间均匀分开,在每天早晨发送给他们,CSR通过电话执行那些请求。系统设计要提高请求的实现速度,所有的顾客在CSR之间共享。顾客的每个请求,无论通过电话或者邮件,都被不同的CSR处理,增加了发生数据并发性问题的机会。

  为了提高性能,应用程序在内存中保持了一个用顾客和订单信息填充的数据集对象。因为很多雇员同时使用该应用程序,就会有许多活动数据的不连接的快照,它们都在雇员的工作站上。所有顾客的维护、订单输入和订单维护都使用名为dsAllData的数据集对象。图1是建立dsAllData的代码,它是全局模块的一部分,因此应用程序中的所有窗体都能使用它。

Const connString = "server=localhost;database=northwind;uid=sa;pwd="
Public connCustSvc As SqlClient.SqlConnection
Public daCustomer As SqlClient.SqlDataAdapter
Public cbCustomer As SqlClient.SqlCommandBuilder
Public daOrders As SqlClient.SqlDataAdapter
Public cbOrders As SqlClient.SqlCommandBuilder
Public daOrderDetail As SqlClient.SqlDataAdapter
Public cbOrderDetail As SqlClient.SqlCommandBuilder
Public dsAllData As DataSet
Public Sub Main()
 connCustSvc = New SqlClient.SqlConnection(connString)
 daCustomer = New SqlClient.SqlDataAdapter("SELECT * FROM Customer", connCustSvc)
 cbCustomer = New SqlClient.SqlCommandBuilder(daCustomer)
 daOrders = New SqlClient.SqlDataAdapter("SELECT * FROM Orders", connCustSvc)
 cbOrders = New SqlClient.SqlCommandBuilder(daOrders)
 daOrderDetail = New SqlClient.SqlDataAdapter("SELECT * FROM OrderDetail", connCustSvc)
 cbOrderDetail = New SqlClient.SqlCommandBuilder(daOrderDetail)
 dsAllData = New DataSet()
 daCustomer.MissingSchemaAction = MissingSchemaAction.AddWithKey
 daCustomer.Fill(dsAllData, "Customer")
 daOrders.MissingSchemaAction = MissingSchemaAction.AddWithKey
 daOrders.Fill(dsAllData, "Orders")
 dsAllData.Tables("Orders").Columns("Total").DefaultValue = 0
 daOrderDetail.MissingSchemaAction =
 MissingSchemaAction.AddWithKey
 daOrderDetail.Fill(dsAllData, "OrderDetail")
Application.Run(New frmCustomerMaintenance())
End Sub

图1.填充数据集对象

  建立dsAllData的代码建立了一个空的数据集对象、三个数据适配器(DataAdapter)和三个命令构造器(CommandBuilder)。每个数据适配器在适当的表上执行一个简单的"SELECT *"操作,而命令构造器用需要的剩余信息填充数据集,使它有插入(insert)、更新(update)和删除(delete)的能力。主程序使用数据适配器对象和所有三个表中的数据填充dsAllData,接着使用Customer Maintenance窗体开始应用程序。
图2显示的是Customer Maintenance屏幕,它有一个绑定到dsAllData的Customers数据表的DataGrid对象。这个简单的表格允许CSR编辑顾客的任意基本属性。因为该表格绑定到了Customers数据表,表格中的任何改变都将自动存储到数据表中。dsAllData将保存这些值,直到CSR点击Save Changes按钮明确地告诉窗体更新下层数据源为止。


图2. Customer Maintenance表格允许编辑

  为了输入订单,使用图3中的代码建立了几个新行并添加到dsAllData中。首先建立一个Order记录,接着在数据表OrderDetail中为订单的每个项建立几个记录。当所有必须的行添加到dsAllData后,一个适当的数据适配器的Update方法调用将用新行更新下层数据源。

Private Sub CreateOrder()
 Dim dr As DataRow
 dr = dsAllData.Tables("Orders").NewRow
 With dr
  .Item("DateOrdered") = Now
  .Item("CustomerID") = 1
  .Item("ShipToAddress") = "123 Main"
  .Item("ShipToCity") = "Kansas City"
  .Item("ShipToState") = "MO"
  .Item("ShipToZip") = "12345"
 End With
 dsAllData.Tables("Orders").Rows.Add(dr)
 AddOrderDetail(dr.Item("ID"), 1, 1, 9.99)
 AddOrderDetail(dr.Item("ID"), 2, 2, 4.99)
 daOrders.Update(dsAllData.Tables("Orders"))
 daOrderDetail.Update(dsAllData.Tables("OrderDetail"))
End Sub

Private Sub AddOrderDetail(ByVal OrderID As Integer, _
ByVal ProductID As Integer, ByVal Quantity As Integer, _
ByVal Price As Single)

 Dim dr As DataRow
 dr = dsAllData.Tables("OrderDetail").NewRow
 With dr
  .Item("OrderID") = OrderID
  .Item("ProductID") = ProductID
  .Item("Quantity") = Quantity
  .Item("Price") = Price
 End With
 dsAllData.Tables("OrderDetail").Rows.Add(dr)
End Sub

图3.有细节记录的新订单

  因为CSR同时使用应用程序和更新顾客信息,因此好像任何时候一个CSR看到的都是过期的信息。为了防止这种现象,应用程序的设计师决定dsAllData的数据缓存要每隔30分钟刷新一次,以确保CSR通常看到正确信息。应用程序设计得很仔细,以确保数据刷新只在空闲时段进行,这样它才不影响性能。数据集刷新因此会比30分钟长一点,这依赖于CSR的行为。


图4.应用程序数据模型

  该应用程序的数据模型很简单(图4)。它使用SQL Server 2000存储,只包含三个表,顾客表、订单表、每个订单的细节表,并定义了适当的主键和关系以确保参照的完整性。此外,在OrderDetail上定义了一个触发器来更新Orders 表的Total列。每次插入、更新或者删除一个OrderDetail记录,调用触发器计算该订单的最后销售值,并更新Orders表的适当的行。图5是trg_UpdateOrderTotal触发器的代码:

CREATE TRIGGER trg_UpdateOrderTotal ON [dbo].[OrderDetail]
FOR INSERT, UPDATE, DELETE
AS
DECLARE @OrderID int
 SELECT @OrderID=OrderID FROM Inserted
 IF @OrderID IS NULL
  SELECT @OrderID=OrderID FROM Deleted
  UPDATE Orders
  SET Total=
  (
   SELECT Sum(Price*Quantity)
   FROM OrderDetail
   WHERE OrderID=@OrderID
  )
WHERE ID=@OrderID

图5.更新Total列
    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章