为什么存储库模式示例从不处理数据库连接异常?
Why do Examples of the Repository Pattern never deal with Database Connection Exceptions?
我阅读了很多教程并看到了很多关于 Repository
模式实现的代码示例。在几乎所有情况下,都没有解决在数据库不可用时尝试访问数据库而导致的异常。考虑到如果数据库位于网络上的某个位置,这是一个非常现实的场景,这似乎很奇怪。
那么处理这些异常的最佳做法是什么?
将这一百个调用中的每一个都包装在一个 try/catch 中,其中每个调用都可能有
相同的 n 捕获块?那就是很多重复,凌乱,容易出错等等
让异常冒泡到应用程序级别并将它们捕获为
未处理的异常?如果在 UI 线程上抛出异常,这是有意义的,否则,处理未处理的 AppDomain 异常会导致应用程序关闭。
使用Enterprise Library's Exception等框架
处理应用程序块?
首先,因为SRP。一个class只负责一个且只有一个职责。至少在某种程度上是这样。
其次,这取决于您需要如何处理失败。您会 向用户显示错误消息 吗? 在应用层处理,因为数据层和业务层不知道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.
老实说,我认为这个问题没有得到解决,因为关于如何处理异常的争论一直在进行(而且情绪激动)。关于异常应该在本地处理(在那里有更大的机会理解它们并做一些聪明的事情,比如重试)还是在 UI 层处理(其中 99.9% 的异常最终冒泡),人们一直在争论不休最多)。
就个人而言,我发现在 Repository 层内执行 try/catch 最优雅,以捕获特定于数据库的异常,并抛出我自己创建的新异常。这给了我一个放置重试逻辑的地方。然后我还可以决定 DAOException 是已检查异常还是运行时异常。
这允许用户界面处理已知的异常,并帮助我将更高级别的层与任何特定于提供程序的错误隔离开来。例如,如果我将我的数据存储迁移到 No-SQL 数据库,如 Mongo 或 Cassandra,我仍然可以抛出相同的异常,并保持它们的语义,而无需更改所有调用代码。
我阅读了很多教程并看到了很多关于 Repository
模式实现的代码示例。在几乎所有情况下,都没有解决在数据库不可用时尝试访问数据库而导致的异常。考虑到如果数据库位于网络上的某个位置,这是一个非常现实的场景,这似乎很奇怪。
那么处理这些异常的最佳做法是什么?
将这一百个调用中的每一个都包装在一个 try/catch 中,其中每个调用都可能有 相同的 n 捕获块?那就是很多重复,凌乱,容易出错等等
让异常冒泡到应用程序级别并将它们捕获为 未处理的异常?如果在 UI 线程上抛出异常,这是有意义的,否则,处理未处理的 AppDomain 异常会导致应用程序关闭。
使用Enterprise Library's Exception等框架 处理应用程序块?
首先,因为SRP。一个class只负责一个且只有一个职责。至少在某种程度上是这样。
其次,这取决于您需要如何处理失败。您会 向用户显示错误消息 吗? 在应用层处理,因为数据层和业务层不知道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.
老实说,我认为这个问题没有得到解决,因为关于如何处理异常的争论一直在进行(而且情绪激动)。关于异常应该在本地处理(在那里有更大的机会理解它们并做一些聪明的事情,比如重试)还是在 UI 层处理(其中 99.9% 的异常最终冒泡),人们一直在争论不休最多)。
就个人而言,我发现在 Repository 层内执行 try/catch 最优雅,以捕获特定于数据库的异常,并抛出我自己创建的新异常。这给了我一个放置重试逻辑的地方。然后我还可以决定 DAOException 是已检查异常还是运行时异常。
这允许用户界面处理已知的异常,并帮助我将更高级别的层与任何特定于提供程序的错误隔离开来。例如,如果我将我的数据存储迁移到 No-SQL 数据库,如 Mongo 或 Cassandra,我仍然可以抛出相同的异常,并保持它们的语义,而无需更改所有调用代码。