我只是想知道您对定义,抛出和捕获异常的位置有什么看法,看看是否有关于考虑的共识。
让我举一个基于的例子。
假设我有解决方案,在该解决方案中,我有2个项目:DomainProject
和ControllerProject
。
在DomainProject中,我有一个存储库,用于在RepositoryClass
中进行一些查询,我有方法:
GetObjectById(int id) { ... }
我在这个项目中有一些异常定义,如ObjectNotFoundException
。
在ControllerProject
我想查询我的存储库,所以我做了一些看起来像这样的事情:
MyObject obj = repo.GetObjectById(11);
现在问题是谁应该注意检查id是否确实存在。如果你选择ControllerProject应该检查是否存在id,你可以得到一些像这样的代码:
MyObject obj = repo.GetObjectById(11);
if (obj == null) {throw new ObjectNotFoundException();}
但缺点是它在使用GetObjectById
的地方往往是重复的。当然,有些情况下你不会在意你是否得到一个空值,所以它在某种程度上是合法的,不会直接在DomainProject中抛出异常。但我首先不喜欢复制if测试,第二,与我的问题更相关,我不喜欢在当前项目之外使用异常定义。
我觉得Exceptions只应该放在定义它的项目中,而其他项目应该只捕获它们。
回到我的例子,我将如何解决这种情况。一个简单的想法是在我的域项目中定义2方法。一个抛出异常,一个抛出异常。我唯一不确定的是我必须使用哪种命名约定:GetObjectByIdThrowsIfNotFound()
和GetObjectById()
。或者我可以添加一个可选参数GetObjectById(int id, bool isExceptionThrow = true)
。
您如何看待异常?
由于
答案 0 :(得分:2)
我认为你很好地考虑如何让你的设计正确地传达意图。我同意你的疑虑:抛出异常的唯一层应该是定义异常的层。
也就是说,如果null返回值不明确,如果将密钥与null
相关联是有效的,则只需要抛出异常的变量。如果不是(这应该在你的XML注释中注明!),那么null返回总是意味着相同的事情(找不到值),你可以在域层中节省代码和处理异常的开销。如果'找不到值'是Controller层中真正异常的事件,请在那里定义并抛出异常。
如果'存储的null
' 有效,我会使用IDictionary<T>
:bool TryGetObjectById(int id, out object value)
建立的语义模式,而且我只包含异常抛出GetObjectById(int key)
变体,如果找不到密钥是真的例外,我想保存在调用Try...
变体时所涉及的击键。
答案 1 :(得分:0)
我经常看到两种模式:
1)存储库本身应该抛出异常,如果你想处理获取项目的失败,你的调用方法应该包装在try / catch块中。我认为这种模式将是您的应用程序的主要候选者,因为调用者负责处理异常。我会假设您的存储库没有捕获SqlException
或任何持久层异常被抛出正确吗?如果是这样,那么你应该让存储库抛出异常并让它冒泡堆栈。
如果不是......
2)您公开包装在容器中的对象,告诉您失败或成功:
public class RepositoryItemContainer<DataType>
{
public DataType Object { get; set; }
public bool WasFound { get; set; }
}
然后,不是只返回值,而是返回此包装器,然后代码可以决定它想要做什么:
var repoItem = _repo.GetObjectById(11);
if(repoItem.WasFound)
var item = repoItem.Object;
else
throw new ApplicationSpecificException("Wasnt found yo!")
答案 2 :(得分:0)
以下是一些注意事项:
Exceptions
适用于例外情况。因此,如果您认为DB中缺少对象是异常情况,我会说现在就实现它。
如果不是例外情况,请以不引发Exception
的方式实施方法,但如果我们查询的null
是id
,则返回ContainsId(long id)
自然错过了。对于将使用您实现的repo类的开发人员来说,似乎很自然并保持预期的行为。
您可以添加id
方法,首先检查数据库中是否存在已请求的{{1}}。也可能是一个不错的选择,但我个人更喜欢第二点。
希望这有帮助。