技术开发 频道

ADO.NET的开发场景及传统ADO的处理

  

  服务器端游标与并发性

  并发性问题在许多企业级应用程序中是合理而且常见的问题,但是使用服务器端游标以解决这一问题的代价却可能非常之高。那么在ADO.NET中有什么替代方式处理并发性问题呢?常见的技术是允许应用程序对数据库提交更改,然后在遇到并发性问题时引发特殊类型的异常。有一个特殊类型的异常名为DBConcurrencyException,它是从Exception基类派生而来的。因为DataSet将每列的原始值和拟更改值存储到一行中,它知道如何将值与数据库进行比较以自动寻找并发性问题。例如,假定用户将firstname字段的值由Lloyd更改为Lamar,并将更改提交给数据库,存储在DataSet中的更改将通过DataAdapter的Update方法传递给数据库。在数据真正保存之前,如果数据库中的名字不再是Lloyd,现在变成了Lorenzo,将会引发一个DBConcurrencyException异常。此异常可以以最适合应用程序需要的任何方式进行捕获和处理,例如,可以采取最后者优先的规则,使用户可以选择取消更改、改写或者强制更新或其他技术。

public DataSet SaveData(DataSet oDs) {  string sMethodName = "[public void SaveData(DataSet oDs)]";  //==========================================================  //-- Establish local variables  //==========================================================  string sProcName;  string sConnString = "Server=(local);Database=Northwind;Integrated  Security=SSPI";  SqlDataAdapter oDa = new SqlDataAdapter();  SqlTransaction oTrn = null;  SqlConnection oCn = null;  SqlCommand oInsCmd = null;  SqlCommand oUpdCmd = null;  SqlCommand oDelCmd = null;  try  {   //==========================================================   //-- Set up the Connection   //==========================================================   oCn = new SqlConnection(sConnString);   //==========================================================   //-- Open the Connection and create the Transaction   //==========================================================   oCn.Open();   oTrn = oCn.BeginTransaction();   //==========================================================   //-- Set up the INSERT Command   //==========================================================   sProcName = "prInsert_Order";   oInsCmd = new SqlCommand(sProcName, oCn, oTrn);   oInsCmd.CommandType = CommandType.StoredProcedure;   oInsCmd.Parameters.Add(new SqlParameter("@sCustomerID",
SqlDbType.NChar,
5, "CustomerID"));   oInsCmd.Parameters.Add(new SqlParameter("@dtOrderDate",   SqlDbType.DateTime, 8,"OrderDate"));   oInsCmd.Parameters.Add(new SqlParameter("@sShipCity",   SqlDbType.NVarChar, 30, "ShipCity"));   oInsCmd.Parameters.Add(new SqlParameter("@sShipCountry",
SqlDbType.NVarChar,
30, "ShipCountry"));   oDa.InsertCommand = oInsCmd;   //==========================================================   //-- Set up the UPDATE Command   //==========================================================   sProcName = "prUpdate_Order";   oUpdCmd = new SqlCommand(sProcName, oCn, oTrn);   oUpdCmd.CommandType = CommandType.StoredProcedure;   oUpdCmd.Parameters.Add(new SqlParameter("@nOrderID",
SqlDbType.Int,
4, "OrderID"));   oUpdCmd.Parameters.Add(new SqlParameter("@dtOrderDate",
SqlDbType.DateTime,
8,"OrderDate"));   oUpdCmd.Parameters.Add(new SqlParameter("@sShipCity",
SqlDbType.NVarChar,
30, "ShipCity"));   oUpdCmd.Parameters.Add(new SqlParameter("@sShipCountry",
SqlDbType.NVarChar,
30, "ShipCountry"));   oDa.UpdateCommand = oUpdCmd;   //==========================================================   //-- Set up the DELETE Command   //==========================================================   sProcName = "prDelete_Order";   oDelCmd = new SqlCommand(sProcName, oCn, oTrn);   oDelCmd.CommandType = CommandType.StoredProcedure;   oDelCmd.Parameters.Add(new SqlParameter("@nOrderID", SqlDbType.Int, 4, "OrderID"));   oDa.DeleteCommand = oDelCmd;   //==========================================================   //-- Save all changes to the database   //==========================================================   oDa.Update(oDs.Tables["Orders"]);   oTrn.Commit();   oCn.Close();  }
 catch (DBConcurrencyException exDBConcurrency)  {   //=======================================================   //-- Roll back the transaction   //=======================================================   oTrn.Rollback();   //--------------------------------------------------------   //-- May want to rethrow the Exception at this point.   //-- This depends on how you want to handle the concurrency   //-- issue.   //--------------------------------------------------------   //-- throw(exDBConcurrency);  }  catch (Exception ex)  {   //==========================================================   //-- Roll back the transaction   //==========================================================   oTrn.Rollback();   //--------------------------------------------------------   //-- Rethrow the Exception   //--------------------------------------------------------   throw;  }  finally  {   oInsCmd.Dispose();   oUpdCmd.Dispose();   oDelCmd.Dispose();   oDa.Dispose();   oTrn.Dispose();   oCn.Dispose();  }  oCn.Close();  return oDs; }
  
  在代码中,可以注意到一个示例方法,它接收一个DataSet参数,并将DataSet中的更改通过DataAdapter的Update方法提交给数据库。如果检测到并发性问题,将由一个特定的catch代码块引发和捕获DBConcurrencyException异常。此时,事务可以回滚,然后还可重新引发异常,直到最终异常被客户端应用程序捕获,从而得以通知用户并询问用户希望如何处理。这里同样要仔细地考虑catch代码块的顺序。例如,如果首先出现一般性的catch代码块,那么它已经捕获了并发性问题。异常处理的关键在于,要确保针对更特定异常的catch代码块在其他catch代码块之前出现,从而可由特定catch代码块首先捕获特定异常。
 
   批量更新
 
  ADO Recordset对象经常会用到的一种情形是进行批量更新。例如,在一个N层应用程序中,ADO Recordset可检索一个行集,将其从数据源中断开连接,并将Recordset发送到客户端层。在客户端应用程序中,对几行的更改将在断开连接的ADO Recordset中进行。然后Recordset可被发回给中间层,通过ADO Connection对象与数据库重新连接,其更改可通过UpdateBatch方法应用于数据库。UpdateBatch方法将接受Recordset中的所有被更改的行,并将更改应用于Recordset源查询中指示的源表。ADO.NET也有内置的处理批量更新的功能。DataSet是一个自成一体的、总是断开连接的行集集合,它可以存储行集中行与列的原始值和当前值。DataSet可以被发送给客户端应用程序,由后者进行更改操作。DataSet然后被发送到中间层,其更改将通过DataAdapter对象应用于数据库。
//-- Setting the ADO.NET DataAdapter's command objects oDa.InsertCommand = oInsCmd; oDa.UpdateCommand = oUpdCmd; oDa.DeleteCommand = oDelCmd; oDa.Update(oDs.Tables["Orders"]);
  
  ADO.NET技术在对数据库应用更新的方式上比传统的ADO批量更新技术更灵活。传统ADO是通过查看ADO Recordset的Source属性来确定保存更改的位置。例如,假定Recordset的源是:
SELECT OrderID, CustomerID, OrderDate, ShipCity, ShipCountry FROM Orders
  
  这种情况下,在对Recordset已经进行批量更改而且调用了Recordset的UpdateBatch方法之后,Recordset必须指向发送更改的位置。它查看源的SQL语句,并确定Orders表的主键(OrderID)在语句中而且只用到了一个表。因此,它可以使用SQL语句创建UPDATE、INSERT和DELETE语句对Orders表进行处理。为了能够派生操作查询,Recordset必须知道唯一行标识符。同样,存储过程不能用于这种技术更新数据库,因为语句是隐式创建的。以下ADO 2.x代码说明了包含Orders的Recordset如何在客户端应用程序中进行更新:
'-- Using traditional ADO, '-- Updating two rows in the client tier oRs.Find "CustomerID = 'VINET'" oRs.Fields("ShipCity") = "Somewhere" oRs.Update oRs.Find "CustomerID = 'CHOPS'" oRs.Fields("ShipCity") = "Elsewhere" oRs.Update

 

 

 

0
相关文章