当来自 db 的数据不好时是不是应该抛出异常?

Posted

技术标签:

【中文标题】当来自 db 的数据不好时是不是应该抛出异常?【英文标题】:Should an exception be thrown when the data from db is bad?当来自 db 的数据不好时是否应该抛出异常? 【发布时间】:2012-04-28 22:02:34 【问题描述】:

在这个特定的应用程序中,没有单独的数据层,数据访问代码在实体本身中。例如,考虑一个客户实体,然后在定义成员和属性的 Customer.cs 文件中,您有如下方法来加载客户对象

public bool TryLoad(int customerID, out Customer customer)

    bool success = false
    try
    
        //code which calls the db and fills a SqlDataReader
        ReadDataFromSqlDataReader(reader);
        success = true;
    
    catch(Exception ex)  
    
      LogError();
      success = false;
    
    return success;

现在,在 ReadDataFromSqlDataReader(reader) 中,tryparse 用于将读取器中的数据加载到对象中。例如

public void ReadDataFromSqlDataReader(reader)

   int.TryParse(reader["CustomerID"].ToString(), out this.CustomerID);
   PhoneNumber.TryParse(reader["PhoneNumber"].ToString(), out this.PhoneNumber);
   ... similar TryParse code for all the other fields..

使用 TryParse 从读取器读取所有属性是一种好习惯吗?一位开发人员告诉我,这样做是因为 TryParse 比 int.Parse 具有更好的性能。但是,当您从数据库读取的值不符合您的代码期望的值时,您不希望抛出异常吗?我的意思是在这种情况下,如果数据库中的电话号码错误,那么可能根本不应该初始化该对象,而不是加载一个带有空电话号码的对象?

【问题讨论】:

对我来说听起来很特别 TryParse 只有在处理不良数据时才具有更好的 Parse 性能。 我无法想象性能有什么不同。它吞下了异常,因此实际上包含了与您在外部使用 .Parse 编写的代码相同的代码。 只有当您尝试将数据库数据转换为不兼容的类型时,解析才会失败,对吧。虽然我猜有原生PhoneNumber类型的数据库并不多 作为,TryParse 返回布尔值。所以,我可以在Boolean值的基础上ByPass/ContinueUpcoming Logic/Any Database请求。我不会让异常发生在这样的场景中。为此,我将进行日志记录等。 【参考方案1】:

作为,TryParse 返回布尔值。所以,我可以在Boolean值的基础上ByPass/ContinueUpcoming Logic/Any Database请求。我不会让异常发生在这样的场景中。为此,我将进行日志记录等。

解析

    引发异常。 如果您确定该值有效,请使用它

TryParse

    返回一个表示是否成功的布尔值 它只是在内部尝试/捕获为什么没有例外地实现,所以它很快 在值可能为 InValid 的情况下使用它

【讨论】:

【参考方案2】:

我不会为此使用 TryParse。 如果我的数据库中有垃圾,我希望得到解决。 如果数据在解析方面不明确(例如,Varchar 带有 int 或 double 作为字符串) 我想整理一下我的架构,TryParse 作为类型检测将是一种快速而简单的技巧。

【讨论】:

【参考方案3】:

将数据插入数据库时​​执行有效的验证。这不是一个好的设计,可以在数据库中输入错误的数据。如果数据库包含错误的电话号码,如果它是强制性的,则应要求用户再次输入电话号码,如果电话号码不是那么重要,您可以将电话号码初始化为 null 以防数据错误。

【讨论】:

【参考方案4】:

是的,遵循 fail-fast 原则,您可能希望在返回无法转换为预期类型的​​值时抛出异常。

如果其中一项操作失败并且您继续执行,就好像什么都没发生一样,您可能会遇到难以捕捉且难以查明的奇怪错误:对象被添加到数据库中,而它们本应更新现有行,数据神秘消失,猫狗生活在一起……你懂的。

另一方面,如果您立即抛出异常,您就可以准确地知道问题出在哪里,并且可以在问题变得更糟之前发现并修复它。

TryParse 在失败的情况下会比Parse 工作得更快,这仅仅是因为它不必抛出异常(这可能很昂贵),但是如果你假设事情会成功(这段代码是不使用解析的结果),您应该使用Parse 获得相同的性能。

【讨论】:

作为,TryParse 返回布尔值。所以,我可以在Boolean值的基础上ByPass/ContinueUpcoming Logic/Any Database请求。我不会让异常发生在这样的场景中。为此,我将进行日志记录等。【参考方案5】:

如果你会处理它,那么是的,你应该抛出一个异常。但是,如果您不关心异常,并且如果您要过滤并使用正确的数据(在这种情况下为非零),您可以跳过。

【讨论】:

我完全不同意。调用此方法的程序员将假定他们获得的对象已完全填充。给他们一个未完全填充的对象是自找麻烦。即使没有处理代码,最好让系统发生严重故障并让您注意到您有错误这一事实,而不是让其余代码像没有任何问题一样使用这些数据。 你是对的,但这是一个选择。抛出或不抛出异常取决于你想要做什么。我说如果你关心你应该抛出一个异常,但可能并非总是如此。也许他想在用户界面的电话号码栏中显示一条消息,例如“您的电话号码无效。”。 如果我这样做并且它无效是一个例外。我会把它扔到调用者中,然后在调用代码中处理它。 (或 mabe 陷阱并引发自定义异常。将消息放入执行解析的代码中,意味着它在做不止一件事,意味着您必须做同样的事情或破坏 DRY,这将导致糟糕的设计,并使任何围绕 UI 相关的单元测试。所以一个选项是的,一个不错的选项,我很怀疑。 捕获每个异常(比如 1000 个项目)并进行处理可能是多余的,而不是为对象的字段分配一个简单的字符串。所以没有一般​​意义上的最好,只有性能、设计、可测试性等方面最好。 这都是相对的,但是如果 1000 条记录是一个显着的百分比,那么我会修复数据并根据性能提升证明这样做是合理的。如果你不得不忍受它,你必须确保它几乎是你唯一的选择,糟糕的设计可能会成为一个坏习惯。【参考方案6】:

在插入和/或更新数据时,您正在执行哪种验证?

只要您在这里应用某种形式的验证,我个人就不会验证来自数据库的数据,因为您应该确信您只输入了有效数据。

【讨论】:

你能 100% 保证它总是好的数据吗?当 dba 直接打开表并输入“stjkerij”作为电话号码时会发生什么? 问题是系统最初开发时没有验证。例如,PhoneNumber 不是代码中定义的类型,它基本上是一个字符串。几年后,其中一位开发人员将 PhoneNumber 定义为一种类型,现在大多数情况下,每当插入数据时,它都是有效数据。但是旧代码的混乱,我不能 100% 确定来自数据库的数据总是有效的。此外,如果我失败并记录这些,我实际上可能能够从数据库中清除不良数据。 @JonH,数据好不好不重要,重要的是你能不能把它做得好,如果不是。

以上是关于当来自 db 的数据不好时是不是应该抛出异常?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我应该在抛出异常指针时使用按引用捕获

当查询具有 GROUP BY 子句时,Hive 查询抛出“来自 org.apache.hadoop.hive.ql.exec.tez.TezTask 的代码 2”异常

测试期间的Spring Boot JPA事务 - 不会在插入时抛出密钥违例异常

Symfony/PhpUnit - 如何测试服务中是不是抛出异常

索引器是不是应该抛出异常[重复]

当文件没有所需权限时抛出异常