ADO.NET错误生成后,将由.NET框架内置的底层结构化异常处理支持所处理。结果,在数据访问代码中的错误处理方式与应用程序中其它地方的错误处理方式完全相同。通过标准的.NET异常处理语法和技术,异常被检测到并被处理。
本节描述了如何开发强壮的数据访问代码,并解释了如何处理数据访问错误。本节还提供了与SQL Server .NET数据供应器相关的异常处理详尽指南。
.NET 异常
.NET数据供应器将特定的数据库的错误状态转化为标准的异常类型,应当在数据访问代码中对这些异常进行处理。通过相关的异常对象的属性,可以获得特定数据库的错误细节。
所有.NET异常类型最终是从System名称空间的Exception基类中派生的。.NET数据供应器释放特定的供应器异常类型。例如,一旦SQL Server 返回一个错误状态时,SQL Server .NET数据供应器释放SqlException对象。类似的,OLE DB .NET数据供应器释放 OleDbException类型的异常,此对象包含了由底层OLE DB供应器暴露的细节。
图3显示了.NET数据供应器异常的层次结构。注意,OleDbException类是从 ExternalException类派生的ExternalException类是所有COM例外的基类。对象的ErrorCode属性存储了OLE DB生成的COM HRESULT。
图3 NET数据供应器层次结构
缓存并处理.NET异常
要处理数据访问例外状态,将数据访问代码放在try块中,并在catch块中利用合适的过滤器捕获生成的任何例外。例如,当利用SQL Server .NET数据供应器编写数据访问代码时,应当捕获SqlException类型的异常,如下面的代码所示:
try { // Data access code } catch (SqlException sqlex) // more specific { } catch (Exception ex) // less specific { }
如果为不止一个catch声明提供了不同的过滤标准,记住,按最特殊类型到最不特殊类型的顺序排列它们。通过这种方式,catch块中最特殊类型将将为任何给定的类型所执行。
SqlException 类所暴露的属性包含了例外状态的细节。其中包括:
- Message属性,它包含了用于描述错误的文本。
- Number属性,它包含唯一标识错误类型的错误号。
- State属性。它包含了关于错误启用状态的附加信息。它经常用于指示特殊错误状态的某个特定事件。例如,如果单一存储过程从不止一行中生成同样的错误,那么本属性将用于标识某个具体的事件。
- Errors集合。它包含了SQL Server生成的错误的详细信息。此集合部是包含至少一个SqlError类型的对象。
下面的代码片段演示了如何利用SQL Server .NET数据供应器处理SQL Server 错误状态:
using System.Data; using System.Data.SqlClient; using System.Diagnostics; // Method exposed by a Data Access Layer (DAL) Component public string GetProductName( int ProductID ) { SqlConnection conn = new SqlConnection( "server=(local);Integrated Security=SSPI;database=northwind"); // Enclose all data access code within a try block try { conn.Open(); SqlCommand cmd = new SqlCommand("LookupProductName", conn ); cmd.CommandType = CommandType.StoredProcedure; cmd.Parameters.Add("@ProductID", ProductID ); SqlParameter paramPN = cmd.Parameters.Add("@ProductName", SqlDbType.VarChar, 40 ); paramPN.Direction = ParameterDirection.Output; cmd.ExecuteNonQuery(); // The finally code is executed before the method returns return paramPN.Value.ToString(); } catch (SqlException sqlex) { // Handle data access exception condition // Log specific exception details LogException(sqlex); // Wrap the current exception in a more relevant // outer exception and re-throw the new exception throw new DALException( "Unknown ProductID: " + ProductID.ToString(), sqlex ); } catch (Exception ex) { // Handle generic exception condition . . . throw ex; } finally { conn.Close(); // Ensures connection is closed } } // Helper routine that logs SqlException details to the // Application event log private void LogException( SqlException sqlex ) { EventLog el = new EventLog(); el.Source = "CustomAppLog"; string strMessage; strMessage = "Exception Number : " + sqlex.Number + "(" + sqlex.Message + ") has occurred"; el.WriteEntry( strMessage ); foreach (SqlError sqle in sqlex.Errors) { strMessage = "Message: " + sqle.Message + " Number: " + sqle.Number + " Procedure: " + sqle.Procedure + " Server: " + sqle.Server + " Source: " + sqle.Source + " State: " + sqle.State + " Severity: " + sqle.Class + " LineNumber: " + sqle.LineNumber; el.WriteEntry( strMessage ); } }
在SqlException catch块中,代码最初利用LogException帮助函数记录错误状态,此函数利用foreach声明枚举了Errors集合中特定于供应器的细节,并将错误细节记录到错误日志中。 Catch块中的代码然后将特定于SQL Server的例外封装在DALException类型的对象中,这样做对调用者的GetProductName方法更具有意义。例外处理程序使用关键字throw将例外传回调用者。