处理sqlexception的核心是使用try-catch捕获异常,并根据ex.number等属性进行精细化处理;2. 常见错误码包括2627/2601(主键/唯一约束冲突)、547(外键约束)、1205(死锁)、-2(超时)等,可通过switch判断并执行对应逻辑;3. 日志记录应包含错误号、消息、堆栈、上下文信息等,使用serilog或nlog等框架提升可维护性;4. 用户提示需将技术错误翻译为友好信息,如“数据已存在”“系统繁忙请重试”等,避免暴露内部细节;5. 对1205、-2等瞬时性错误应实现重试机制,推荐指数退避加最大重试次数策略;6. 在事务中发生异常时必须回滚事务以保证数据一致性,确保using块正确释放资源。完整的异常处理机制应结合日志、用户提示、重试与回滚,提升系统健壮性与用户体验。

处理C#中的
SqlException
try-catch
ErrorCode
Message
当我们在C#应用中与SQL Server数据库交互时,各种问题都可能导致
SqlException
要妥善处理它,我们通常会这样做:
using System;
using System.Data.SqlClient; // 注意:这是旧的命名空间,推荐使用 Microsoft.Data.SqlClient
public class DbOperations
{
public void InsertData(string connectionString, string data)
{
string sql = "INSERT INTO MyTable (Column1) VALUES (@data)";
try
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
using (SqlCommand command = new SqlCommand(sql, connection))
{
command.Parameters.AddWithValue("@data", data);
command.ExecuteNonQuery();
Console.WriteLine("数据插入成功。");
}
}
}
catch (SqlException ex)
{
// 捕获到SqlException
Console.WriteLine($"数据库操作失败:{ex.Message}");
Console.WriteLine($"错误号:{ex.Number}");
Console.WriteLine($"错误源:{ex.Source}");
Console.WriteLine($"存储过程/命令:{ex.Procedure}");
// 可以进一步检查ex.Errors集合,获取更详细的错误信息
foreach (SqlError error in ex.Errors)
{
Console.WriteLine($"详细错误:{error.Message} (Line: {error.LineNumber}, State: {error.State})");
}
// 根据错误类型进行更精细的处理(下面会详细讲到)
// 例如:记录日志、向用户显示友好信息、尝试重试等
LogDatabaseError(ex);
DisplayUserFriendlyError(ex.Number);
}
catch (Exception ex)
{
// 捕获其他非SqlException的异常,比如网络问题、内存不足等
Console.WriteLine($"发生未知错误:{ex.Message}");
LogGeneralError(ex);
}
}
private void LogDatabaseError(SqlException ex)
{
// 实际应用中,这里会使用日志框架(如Serilog, NLog)记录到文件、数据库或日志服务
Console.WriteLine($"[LOG] SqlException occurred: {ex.Message} (Number: {ex.Number}) at {DateTime.Now}");
// 记录完整的StackTrace对于调试至关重要
Console.WriteLine($"[LOG] StackTrace: {ex.StackTrace}");
}
private void DisplayUserFriendlyError(int errorCode)
{
string userMessage;
switch (errorCode)
{
case 2627: // 主键冲突
case 2601: // 唯一约束冲突
userMessage = "您输入的数据已存在,请检查后重试。";
break;
case 547: // 外键约束冲突
userMessage = "关联数据不存在或无法删除,请检查。";
break;
case 18456: // 登录失败
userMessage = "数据库连接失败,请联系管理员。";
break;
case 1205: // 死锁
userMessage = "当前操作繁忙,请稍后再试。";
// 对于死锁,可能考虑重试机制
break;
default:
userMessage = "数据库操作失败,请联系技术支持。";
break;
}
Console.WriteLine($"提示用户:{userMessage}");
}
}这个例子展示了基本的捕获和一些属性的访问。关键在于,我们不仅仅是捕获,还要深入理解
SqlException
Number
Errors
SqlException
Number
这里列举一些我们日常开发中经常会碰到的:
在代码中,我们可以利用
switch
if-else if
ex.Number
// 假设ex是捕获到的SqlException
switch (ex.Number)
{
case 2627: // 主键冲突
case 2601: // 唯一约束冲突
// 记录详细日志
Log.Warning($"Duplicate entry attempt: {ex.Message}");
// 告知用户
throw new UserFriendlyException("该记录已存在,请勿重复添加。", ex);
case 547: // 外键约束
Log.Warning($"Foreign key constraint violation: {ex.Message}");
throw new UserFriendlyException("关联数据不存在或无法操作。", ex);
case 1205: // 死锁
Log.Error($"Deadlock detected: {ex.Message}");
// 考虑重试,或者提示用户稍后重试
throw new TransientDatabaseException("系统繁忙,请稍后再试。", ex);
case -2: // 超时
Log.Error($"SQL Command Timeout: {ex.Message}");
throw new TransientDatabaseException("操作超时,请检查网络或稍后重试。", ex);
// ... 其他错误码
default:
// 对于不明确的错误,记录详细日志并抛出通用异常
Log.Error($"Unhandled SqlException ({ex.Number}): {ex.Message}", ex);
throw new ApplicationException("数据库操作发生未知错误,请联系管理员。", ex);
}这种细致的区分处理,能让我们的应用在面对数据库问题时,表现得更加“智能”和“人性化”。
日志记录,在我看来,是任何健壮应用不可或缺的眼睛和耳朵。它能帮助我们在生产环境中追踪问题、分析性能瓶颈,甚至发现潜在的安全漏洞。而用户友好提示,则是应用程序的“嘴巴”,它决定了用户在遇到问题时的体验是沮丧还是理解。
关于日志记录:
当
SqlException
ex.Message
ex.GetType().Name
ex.Message
ex.Number
ex.Source
ex.Procedure
ex.Errors
ex.StackTrace
实际项目中,我们会使用成熟的日志框架,比如Serilog、NLog或log4net。它们提供了灵活的配置,可以将日志输出到文件、数据库、ELK Stack、Azure Application Insights等,并支持日志级别(Info, Warning, Error, Fatal)的区分。
// 示例:使用伪代码展示日志记录
public static class AppLogger
{
public static void LogError(Exception ex, string contextMessage = "")
{
// 实际这里会调用日志框架的方法
Console.Error.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] ERROR: {contextMessage}");
if (ex is SqlException sqlEx)
{
Console.Error.WriteLine($" SqlException Details:");
Console.Error.WriteLine($" Message: {sqlEx.Message}");
Console.Error.WriteLine($" Number: {sqlEx.Number}");
Console.Error.WriteLine($" Source: {sqlEx.Source}");
Console.Error.WriteLine($" Procedure: {sqlEx.Procedure}");
foreach (SqlError error in sqlEx.Errors)
{
Console.Error.WriteLine($" Sub-error: {error.Message} (Line: {error.LineNumber}, State: {error.State})");
}
}
else
{
Console.Error.WriteLine($" General Exception Details: {ex.Message}");
}
Console.Error.WriteLine($" StackTrace: {ex.StackTrace}");
// 考虑记录InnerException
if (ex.InnerException != null)
{
Console.Error.WriteLine($" Inner Exception: {ex.InnerException.Message}");
}
}
}
// 在catch块中调用:AppLogger.LogError(ex, "Failed to insert user data.");关于用户友好提示:
直接将
SqlException.Message
原则是:将技术错误翻译成业务语言。
通过这种方式,我们不仅保护了系统的内部细节,也提升了用户体验,让用户感到应用是可靠和专业的。
在处理
SqlException
何时考虑重试机制?
重试机制并非万金油,它有明确的适用场景——主要针对瞬时性错误 (Transient Errors)。这类错误通常是由于网络波动、数据库暂时性不可用、资源争用(如死锁)等原因造成的,它们在短时间内可能会自行恢复。
常见的需要考虑重试的
SqlException.Number
实现重试的策略:
// 伪代码:一个简单的重试逻辑
public void SafeDatabaseOperation(string connectionString, string sql)
{
int maxRetries = 3;
TimeSpan delay = TimeSpan.FromSeconds(1); // 初始延迟
for (int i = 0; i < maxRetries; i++)
{
try
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
using (SqlCommand command = new SqlCommand(sql, connection))
{
command.ExecuteNonQuery();
Console.WriteLine("操作成功。");
return; // 成功则退出
}
}
}
catch (SqlException ex)
{
// 检查是否是瞬时错误
if (ex.Number == 1205 || ex.Number == 40613 || ex.Number == -2 /* ...更多瞬时错误码 */)
{
if (i < maxRetries - 1)
{
Console.WriteLine($"检测到瞬时错误 ({ex.Number}),第 {i + 1} 次重试,等待 {delay.TotalSeconds} 秒...");
Thread.Sleep(delay);
delay = delay * 2; // 指数退避
continue; // 继续下一次循环进行重试
}
}
// 非瞬时错误或达到最大重试次数,则抛出
LogDatabaseError(ex);
throw;
}
catch (Exception ex)
{
LogGeneralError(ex);
throw;
}
}
}在更复杂的场景下,可以考虑使用Polly这样的开源库,它提供了强大的弹性策略(包括重试、断路器等)。
何时考虑事务回滚?
事务的目的是确保一组数据库操作要么全部成功,要么全部失败,从而维护数据的一致性。当在事务内部发生
SqlException
如果事务中的任何一个操作失败(抛出
SqlException
using System.Data; // For IsolationLevel
public void PerformTransactionalOperation(string connectionString, string data1, string data2)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
SqlTransaction transaction = null; // 声明事务对象
try
{
// 开启事务
transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted);
// 第一个操作
using以上就是C#的SqlException怎么处理?数据库异常捕获的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号