SQL Server错误严重性级别和异常处理
关于SQL Server的错误严重性级别的说明,强烈认真看一下下面的两个链接
脱机帮助
ms-help://MS.SQLCC.v9/MS.SQLSVR.v9.zh-CHS/sqlerrm9/html/3e7f5925-6edd-42e1-bf17-f7deb03993a7.htm
在线帮助
http://technet.microsoft.com/zh-cn/library/ms164086.aspx
简而言之,SQL Server的错误严重性分为三个等级
1. 轻微错误 :严重性级别为0-10
2. 中等错误 :严重性级别为11-19
3. 严重错误 :严重性级别为20-25
SQL Server保存的所有错误消息是可以通过检索sys.messages这个视图来查看的,如下
为什么需要说这个呢?是因为我们需要知道不同的错误严重性级别所导致的行为是不一样的
1. 轻微错误 :严重性级别为0-10 ==》默认情况下不会给客户程序发送错误消息,继续工作。也就是说它无法被CATCH到
2. 中等错误 :严重性级别为11-19 ==》能够被CATCH到(不管是在T-SQL里面还是在客户程序里面)
3. 严重错误 :严重性级别为20-25 ==》SQL Server将强制把连接关掉。很显然这也不可能被CATCH到
关于在T-SQL中使用TRY...CATCH处理异常,请参考下面的链接
http://technet.microsoft.com/zh-cn/library/ms179296.aspx
关于在客户端程序中处理异常,分两种情况
1. 对于错误严重性级别在11-19之间的,能够被TRY...CATCH到SQLException类型,然后可以对其进行处理
http://msdn.microsoft.com/zh-cn/library/system.data.sqlclient.sqlexception(VS.80).aspx
2. 对于错误严重性级别在0-10之间的系统错误,或者通过PRINT语句发出的消息,或者通过RAISERROR语句发出的错误,如果需要在客户程序里面处理,那么应该编写Connection对象的InfoMessage事件
http://msdn.microsoft.com/zh-tw/library/a0hee08w.aspx
关于infoMessage事件,因为它可以接受PRINT语句的输出消息,所以也有朋友用它来跟踪存储过程的进度等等,下面就有一个范例
http://www.cnblogs.com/hackzai/archive/2005/04/07/133635.html
还有一个属性很有意思,FireInfoMessageEventOnUserErrors 。这个属性为true,那么除了0-10的会被该事件处理,11-19的也可以通过该事件来处理,而不需要用TRY...CATCH
下面是一个例子
private void btTest_Click(object sender, EventArgs e)
{
using (SqlConnection conn = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["Northwind"].ConnectionString))
{
conn.FireInfoMessageEventOnUserErrors = true;
conn.InfoMessage += new SqlInfoMessageEventHandler(conn_InfoMessage);
SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = "DROP TABLE TABLE1"; //这里我是故意让它出错,因为表不存在。由于前面设置了FireInfoMessageEventOnUserErrors为true,所以会自动由infoMessage事件处理
conn.Open();
cmd.ExecuteNonQuery();
cmd.CommandText = "RAISERROR('This is the message from the RAISERROR statement', 10, 1)";
cmd.ExecuteNonQuery();
conn.Close();
}
}
void conn_InfoMessage(object sender, SqlInfoMessageEventArgs e)
{
MessageBox.Show(string.Format("Source:{0},Message:{1}", e.Source, e.Message));
}
值得注意的是,不管是SQLException还是InfoMessage中的事件参数SqlInfoMessageEventArgs,它们都包含一个Errors的集合,里面包含了所有的错误实例。一个完善的异常处理,应该遍历它们。例如下面这样
public static void ShowSqlException(string connectionString)
{
string queryString = "EXECUTE NonExistantStoredProcedure";
StringBuilder errorMessages = new StringBuilder();
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlCommand command = new SqlCommand(queryString, connection);
try
{
command.Connection.Open();
command.ExecuteNonQuery();
}
catch (SqlException ex)
{
for (int i = 0; i < ex.Errors.Count; i++)
{
errorMessages.Append("Index #" + i + "\n" +
"Message: " + ex.Errors[i].Message + "\n" +
"LineNumber: " + ex.Errors[i].LineNumber + "\n" +
"Source: " + ex.Errors[i].Source + "\n" +
"Procedure: " + ex.Errors[i].Procedure + "\n");
}
Console.WriteLine(errorMessages.ToString());
}
}
}