.NET Core7中OdbcConnection如何捕获数据库警告并抛出异常?
问题解答
你的操作是否存在问题?
不存在问题。这是ODBC驱动的默认行为:只有严重的SQL执行错误(如对象不存在、语法错误)才会触发OdbcException;而数据截断、主键冲突这类数据库级别的警告,属于非致命性反馈,驱动不会主动抛出异常,代码会继续执行。
如何获取警告并转为OdbcException抛出?
可以通过以下两种方式实现:
方法1:订阅OdbcConnection的InfoMessage事件
ODBC连接会在收到数据库警告时触发InfoMessage事件,我们可以订阅该事件收集警告信息,之后将其转为异常抛出。
修改你的DBConnect类,添加警告收集逻辑:
using System.Data.Common; using System.Data.Odbc; using System.Data; using System.Collections.Generic; public class DBConnect : IDisposable { private OdbcConnection _database; private List<string> _warningMessages = new List<string>(); // 用于收集警告信息 public DBConnect() { // 初始化连接时订阅InfoMessage事件 _database = new OdbcConnection("{CONNECTION_STRING}"); _database.InfoMessage += OnInfoMessage; } private void OnInfoMessage(object sender, OdbcInfoMessageEventArgs e) { // 收集所有警告消息 foreach (OdbcError error in e.Errors) { _warningMessages.Add(error.Message); } } public async Task PlaceHolderOperation(string commandStr) { CheckConnection(); // 每次执行命令前清空之前的警告 _warningMessages.Clear(); OdbcCommand command = new(commandStr, _database); var perform = command.ExecuteNonQueryAsync(); if (await Task.WhenAny(perform, Task.Delay(TimeSpan.FromMinutes(1.5))) != perform) { throw new TimeoutException($"Database didn't returned within expected limit time: 1.5 minutes"); } // 检查是否有警告,有则抛出异常 if (_warningMessages.Count > 0) { var exceptions = _warningMessages.Select(msg => new OdbcException(msg)).ToArray(); throw new AggregateException("执行SQL时收到数据库警告", exceptions); } } protected void CheckConnection() { if (_database.State == ConnectionState.Closed) { var openning = _database.OpenAsync(); if (Task.WhenAny(openning, Task.Delay(TimeSpan.FromMinutes(1.5))).Result != openning) { throw new TimeoutException($"Database didn't returned within expected limit time: 1.5 minutes"); } } } public void Dispose() { // 取消事件订阅,避免内存泄漏 _database.InfoMessage -= OnInfoMessage; if (_database.State == ConnectionState.Open) { _database.Close(); } _database.Dispose(); } public static async Task Main() { using (var connection = new DBConnect()) { await connection.PlaceHolderOperation("placeholder_command"); } } }
方法2:针对特定数据库设置,将警告转为错误
部分数据库支持通过SQL语句将警告转为错误,以SQL Server为例,可以在执行目标SQL前先执行:
SET ANSI_WARNINGS ON; SET ARITHABORT ON;
这两个设置会让数据库将数据截断、主键冲突这类警告转为错误,此时ODBC驱动就会抛出OdbcException。
你可以修改PlaceHolderOperation方法,在执行目标命令前先执行这些设置:
public async Task PlaceHolderOperation(string commandStr) { CheckConnection(); // 先执行数据库设置命令 using (var setupCommand = new OdbcCommand("SET ANSI_WARNINGS ON; SET ARITHABORT ON;", _database)) { await setupCommand.ExecuteNonQueryAsync(); } OdbcCommand command = new(commandStr, _database); var perform = command.ExecuteNonQueryAsync(); if (await Task.WhenAny(perform, Task.Delay(TimeSpan.FromMinutes(1.5))) != perform) { throw new TimeoutException($"Database didn't returned within expected limit time: 1.5 minutes"); } }
注意:这种方式依赖具体数据库的支持,不同数据库的设置语句可能不同,比如MySQL可以使用
SET sql_mode = 'STRICT_ALL_TABLES';来实现类似效果。
内容的提问来源于stack exchange,提问作者James Jonatah
相关产品推荐
相关产品推荐

