You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 12:47:45