何时使用 Microsoft.Data.SqlClient.SqlException 与 System.Data.SqlClient.SqlException?

Posted

技术标签:

【中文标题】何时使用 Microsoft.Data.SqlClient.SqlException 与 System.Data.SqlClient.SqlException?【英文标题】:When to use Microsoft.Data.SqlClient.SqlException vs System.Data.SqlClient.SqlException? 【发布时间】:2021-12-24 17:10:33 【问题描述】:

我知道2019 Microsoft created Microsoft.Data.SqlClient 是 System.Data.SqlClient 的替代品。 System.Data.SqlClient 将得到持续支持,但新的开发和功能都将在 Microsoft.Data.SqlClient 中。这两个库都有一个名为“SqlException”的类。

假设我在任何地方都在使用 Microsoft.Data.SqlClient,那么可能相关的异常将是 Microsoft.Data.SqlClient.SqlException 类型,但是我使用了一堆 3rd-party 库,怎么能我确定他们会提出Microsoft.Data.SqlClient.SqlException 还是System.Data.SqlClient.SqlException?这是否意味着在某些情况下我有 Microsoft.Data.SqlClient.SqlException 的捕获处理程序,我还应该检查 System.Data.SqlClient.SqlException?还是发生了什么聪明的事情,这意味着我只需要考虑 Microsoft.Data.SqlClient.SqlException?

例如我有一些旧代码,有点像我在下面显示的,是在我们开始使用 Microsoft.Data.SqlClient 之前编写的。我担心如果我只是将其更改为使用 Microsoft.Data.SqlClient 则会出现一些 System.Data.SqlClient.SqlException 异常,而我的代码将不再注意到它们。

    private static bool HandleSqlExceptionInSomeWay(Exception ex)
    
        var se = ex as System.Data.SqlClient.SqlException;

        if (se != null)
        
            // ... do something with the SqlException
            return true;
        

        return false;
    

所以我应该把它改成这样,即分别检查不同的类型吗?

    private static bool HandleSqlExceptionInSomeWay(Exception ex)
    
        // handle old SqlExceptions (e.g. from any old 
        // libraries not using MS package yet)
        var se1 = ex as System.Data.SqlClient.SqlException;

        if (se1 != null)
        
            // ... do something with the SqlException ...

            return true;
        

        // handle shiny new SqlExceptions 
        var se2 = ex as Microsoft.Data.SqlClient.SqlException;

        if (se2 != null)
        
            // ... do something with the SqlException ... 

            return true;
        

        return false;
    

【问题讨论】:

尽可能使用新库。大多数 NuGet 包已经使用 Microsoft.Data.SqlClient.SqlException how can I be sure whether 他们使用哪个包?如果他们使用旧库,请升级到使用新库的版本。 我想如果你只是捕捉Microsoft.Data.SqlClient.SqlException,你的“捕捉所有全局日志记录处理程序”将开始看到System.Data.SqlClient.SqlException,如果依赖项正在抛出它? 理论上,库应该记录其方法可以抛出的异常类型(例如,使用<exception cref="..."> xmldoc)。这将是方法契约的一部分,因此,更改这将是一个重大更改,需要在语义版本控制中使用新的主要版本号,并在发行说明中提及。然而,在实践中,理论和实践并不总是一致... 顺便说一句,您的var se1 = ex as System.Data.SqlClient.SqlException; if (se1 != null) 可能更易读为if(ex is System.Data.SqlClient.SqlException sex) ... 【参考方案1】:

这两个类是不同的,但它们确实继承自同一个基类DbException。这是所有数据库异常的通用类,不会具有两个派生类中的所有属性

您应该检查您使用的库/NuGet 包,并确保您使用支持新 Microsoft.Data.SqlClient 库的版本。混淆数据提供者并不好玩,应尽可能避免。大多数流行的 NuGet 包已经使用 Microsoft.Data.SqlClient。

如果您不能这样做,选项取决于您实际处理数据库异常的方式。您是否检查 SQL Server 特定的属性?

另一种选择是推迟升级,直到所有 NuGet 包也都升级完毕。这两个库都包含在部署期间需要包含的本机 DLL。如果混合库,则必须包含所有本机 DLL。

这可能会很痛苦。

处理异常

如果两个库都需要使用,则需要分别处理每种异常类型。模式匹配使这更容易一些:

switch (ex) 

    case  System.Data.SqlClient.SqlException exc:
        HandleOldException(exc);
        return true;
    case Microsoft.Data.SqlClient.SqlException exc:
        HandleNewException(exc);
        return true;
    case DbException exc:
        HandleDbException(exc);
        return true;
    default:
        return false;

映射异常

另一种选择是将这两种异常类型映射到包含有趣属性的新自定义类型。您必须同时映射 SqlExceptionSqlError 类。使用 AutoMapper 会更容易:

var configuration = new MapperConfiguration(cfg => 
    cfg.CreateMap<System.Data.SqlClient.SqlException, mysqlException>();
    cfg.CreateMap<System.Data.SqlClient.SqlError, MySqlError>();
    cfg.CreateMap<Microsoft.Data.SqlClient.SqlException, MySqlException>();
    cfg.CreateMap<Microsoft.Data.SqlClient.SqlError, MySqlError>();
);

这将允许将任一异常映射到常见的MySqlException 类型:

var commonExp=mapper.Map<MySqlException>(ex);

【讨论】:

这 - 如果您有一个在过去 2 年内没有更新过的库,请考虑升级到最新版本(如果有)或考虑删除它。 谢谢,但是有没有办法知道一个包是否使用了新库,而不是开源并且我检查代码? @Rory NuGet 包为您提供依赖项列表。如果您不使用 NuGet 包,请通过 ILSpy 打开 dll 文件,这将为您提供依赖项 @CamiloTerevinto - 对于许多内部应用程序来说,使用 2 年的库并非罕见,不幸的是升级或更改库太昂贵了。 @Rory 我很清楚这一点,但它通常也比与过时库相关的安全风险便宜得多 :) 这都是关于权衡,这就是我说“考虑”的原因跨度> 【参考方案2】:

为了扩展 Richard 的答案,这意味着您的 try/catch 最终看起来像:

try
  //boom
 catch(Microsoft.Data.SqlClient.SqlException ex)
  Handler(ex);
 catch (System.Data.SqlClient.SqlException ex) 
  Handler(ex);

如果你想同时处理这两种情况..

Handler 是一些处理每种类型的可访问的重载(或父类型参数)方法。不是最漂亮的,但是唉,没有办法让一个 catch 捕获 N 不同的类型,除非这些类型都有可以被捕获的相同可用父级(然后根据需要检查异常的类型)..

编辑;完全忘记了catch when,这对最后一种情况很有用,谢谢@Heinzi

try
  //boom
 catch (DbException ex) 
  when (ex is Microsoft.Data.SqlClient.SqlException || ex is System.Data.SqlClient.SqlException)

  //handle

【讨论】:

或者,如果您只需要一个 catch 块并且不需要特定的异常属性:catch (Exception ex) when (ex is Microsoft.Data.SqlClient.SqlException || ex is System.Data.SqlClient.SqlException) @Heinzi 那时我会将它们重命名为 using OldSqlException = System.Data.SqlClient.SqlException; using NewSqlException = Microsoft.Data.SqlClient.SqlException; :D 我不认为最后一个工作,因为你得到一个编译器错误,它可能没有被初始化。 Tbh 我最喜欢第一个版本,一个具体类型的异常处理程序。 Handler 方法可以使用派生自System.Data.Common.DbException(已经有ErrorCode)的包装类来映射SqlException 类型中的属性。 @TimSchmelter 我没有测试它,但我过去使用相同的语法将 DTO 与不同的包区分开来。我也更喜欢 Handler 在内部处理这个问题【参考方案3】:

如果您不能确定第三方库使用的是Microsoft 还是System 库,则需要同时处理这两个库。没有“魔法酱”可以将一个库中的 SqlException 转换为另一个库中的 SqlException

【讨论】:

谢谢。我希望魔法酱能被更广泛地使用,它很好吃。

以上是关于何时使用 Microsoft.Data.SqlClient.SqlException 与 System.Data.SqlClient.SqlException?的主要内容,如果未能解决你的问题,请参考以下文章

MySQL索引的分类何时使用何时不使用何时失效?

何时使用,何时不使用 Try Catch Final

何时使用 Storyboard 以及何时使用 XIB

何时使用 GetXXX() 方法以及何时使用 Getter 属性

Vuex - 何时使用 getter 以及何时使用状态

何时使用 .First 以及何时将 .FirstOrDefault 与 LINQ 一起使用?