【问题标题】:Why do Examples of the Repository Pattern never deal with Database Connection Exceptions?为什么存储库模式的示例从不处理数据库连接异常?
【发布时间】:2015-07-18 16:41:03
【问题描述】:

我阅读了很多教程并看到了很多关于Repository 模式实现的代码示例。在几乎所有情况下,在数据库不可用时尝试访问数据库所导致的异常都没有得到解决。考虑到如果数据库位于网络上的某个地方,这是一个非常现实的场景,这似乎很奇怪。

那么处理这些异常的最佳做法是什么?

  • 将这一百个调用中的每一个都包装在一个 try/catch 中,每个调用都可能有 相同的 n 个捕获块?这是很多重复、混乱、容易出错等。

  • 让异常冒泡到应用程序级别并将它们捕获为 未处理的异常?如果在 UI 线程上引发异常,这是有道理的,否则,处理未处理的 AppDomain 异常会导致应用程序关闭。

  • 使用 Enterprise Library 的 Exception 等框架 处理应用程序块?

【问题讨论】:

  • 许多错误无法在存储库级别重试。例如死锁、超时、网络错误必须在可以跨越多个 repo 操作的事务级别重试。

标签: architecture exception-handling repository-pattern


【解决方案1】:

老实说,我认为这个问题没有得到解决,因为关于如何处理异常的持续(和情绪化)辩论。关于异常应该在本地处理(有更大的机会理解它们并做一些智能的事情,比如重试)还是在 UI 层处理(99.9% 的异常最终会冒泡),一直存在着反复。

就我个人而言,我发现在 Repository 层中执行 try/catch、捕获特定于数据库的异常并抛出我自己制作的新异常是最优雅的。这给了我一个放置重试逻辑的地方。然后,我还可以决定 DAOException 是已检查异常还是运行时异常。

这允许用户界面处理已知异常,并帮助我将更高级别的层与任何特定于提供程序的错误隔离开来。例如,如果我将我的数据存储迁移到像 Mongo 或 Cassandra 这样的 No-SQL 数据库,我仍然可以抛出相同的异常,并保持它们的语义,而无需更改所有调用代码。

【讨论】:

  • 所以每个存储库方法都会有几乎相同的 try/catch 结构?在我的应用程序(实体框架)中,无论对错,每次我访问数据库时,我都有一个单独的catch 用于System.Data.Entity.Core.EntityCommandExecutionExceptionSystem.Data.Entity.Core.EntityException。如果我找到要处理的第三种异常类型,似乎会有很多更新要做......
  • 是的,但关键是您只有在更改数据层时才需要处理它。您将必须在数据层中更改它。而不是让它向上传播,并且必须在所有层上处理它。将数据库职责保留在 DAO 层中。
  • 但是不同的 UI 以不同的方式处理错误(例如 WPF 应用程序弹出消息框但控制台应用程序写入控制台等)呢?
  • 这就是为什么我抛出我自己的异常。所以我可以处理一个已知的异常类型。
【解决方案2】:

首先,因为SRP。一个类只处理一项且仅一项职责。至少在某种方法上是这样。

其次,这取决于您需要如何处理失败。你会只向用户显示错误消息吗? 在应用程序级别处理它,因为数据级别和业务级别不知道那里的 UI。

如果你有一个逻辑,例如:如果无法访问数据库,则使用离线缓存,使用Decorator pattern或类似的方式处理,例如:

public class OnlineUserRepository : IUserRepository{
    public User Get(){ /* get the user from online source */ }
}

public class OfflineUserRepository : IUserRepository{
    public User Get(){ /* get the user from offline source */ }
}

public class UserRepository : IUserRepository{
    public UserRepository(IUserRepository onlineRepo, IUserRepository offlineRepo){
        //parameter assignment
    }
    IUserRepository onlineRepo;
    IUserRepository offlineRepo;
    public User Get(){
        try{
            onlineRepo.Get();
        }
        catch{
            return offlineRepo.Get();
        }
    }
}

为什么我们需要这样处理?同样,因为SRP

【讨论】:

    猜你喜欢
    • 2022-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多