【发布时间】:2009-06-27 17:10:40
【问题描述】:
我正在使用一个用于创建数据库连接的小例程:
之前
public DbConnection GetConnection(String connectionName)
{
ConnectionStringSettings cs= ConfigurationManager.ConnectionStrings[connectionName];
DbProviderFactory factory = DbProviderFactories.GetFactory(cs.ProviderName);
DbConnection conn = factory.CreateConnection();
conn.ConnectionString = cs.ConnectionString;
conn.Open();
return conn;
}
然后我开始查看 .NET 框架文档,了解各种事物的记录行为是什么,并看看我是否可以处理它们。
例如:
ConfigurationManager.ConnectionStrings...
documentation 表示如果无法检索集合,调用 ConnectionStrings 会引发 ConfigurationErrorException。在这种情况下,我无法处理这个异常,所以我会放手。
下一部分是 ConnectionStrings 的实际索引以查找 connectionName:
...ConnectionStrings[connectionName];
在这种情况下,ConnectionStrings documentation 表示如果找不到连接名称,该属性将返回 null。我可以检查是否发生了这种情况,并抛出一个异常让某人高高在上,他们给出了一个无效的连接名称:
ConnectionStringSettings cs=
ConfigurationManager.ConnectionStrings[connectionName];
if (cs == null)
throw new ArgumentException("Could not find connection string \""+connectionName+"\"");
我重复同样的练习:
DbProviderFactory factory =
DbProviderFactories.GetFactory(cs.ProviderName);
GetFactory 方法没有文档说明如果找不到指定 ProviderName 的工厂会发生什么情况。没有记录返回null,但我仍然可以防御,检查是否为空:
DbProviderFactory factory =
DbProviderFactories.GetFactory(cs.ProviderName);
if (factory == null)
throw new Exception("Could not obtain factory for provider \""+cs.ProviderName+"\"");
接下来是 DbConnection 对象的构造:
DbConnection conn = factory.CreateConnection()
documentation 再次没有说明如果它无法创建连接会发生什么,但我可以再次检查 null 返回对象:
DbConnection conn = factory.CreateConnection()
if (conn == null)
throw new Exception.Create("Connection factory did not return a connection object");
接下来是设置 Connection 对象的属性:
conn.ConnectionString = cs.ConnectionString;
文档没有说明如果无法设置连接字符串会发生什么。它会抛出异常吗?它会忽略它吗?与大多数例外情况一样,如果在尝试设置连接的 ConnectionString 时出现错误,我无法从中恢复。所以我什么都不做。
最后,打开数据库连接:
conn.Open();
DbConnection 的Open method 是抽象的,因此由 DbConnection 的任何提供者来决定它们抛出什么异常。抽象的开放方法文档中也没有关于如果出现错误我会发生什么的指导。如果连接出现错误,我知道我无法处理它 - 我必须让它冒泡,调用者可以向用户显示一些 UI,然后让他们再试一次。
之后
public DbConnection GetConnection(String connectionName)
{
//Get the connection string info from web.config
ConnectionStringSettings cs= ConfigurationManager.ConnectionStrings[connectionName];
//documented to return null if it couldn't be found
if (cs == null)
throw new ArgumentException("Could not find connection string \""+connectionName+"\"");
//Get the factory for the given provider (e.g. "System.Data.SqlClient")
DbProviderFactory factory = DbProviderFactories.GetFactory(cs.ProviderName);
//Undefined behaviour if GetFactory couldn't find a provider.
//Defensive test for null factory anyway
if (factory == null)
throw new Exception("Could not obtain factory for provider \""+cs.ProviderName+"\"");
//Have the factory give us the right connection object
DbConnection conn = factory.CreateConnection();
//Undefined behaviour if CreateConnection failed
//Defensive test for null connection anyway
if (conn == null)
throw new Exception("Could not obtain connection from factory");
//Knowing the connection string, open the connection
conn.ConnectionString = cs.ConnectionString;
conn.Open()
return conn;
}
总结
所以我的四行函数变成了 12 行,并且需要 5 分钟的文档查找。最后,我确实发现了一种允许方法返回 null 的情况。但实际上我所做的只是将访问冲突异常(如果我尝试在空引用上调用方法)转换为 InvalidArgumentException。
我还发现了两种可能存在 null 返回对象的情况;但我又一次只用一个例外换了另一个。
从积极的方面来说,它确实发现了两个问题,并解释了异常消息中发生的事情,而不是在路上发生的坏事(即责任止步于此)
但这值得吗?这是矫枉过正吗?这种防御性编程是否出错了?
【问题讨论】:
-
这很好。我唯一不同的是使用 String.Format 来构建消息而不是附加字符串。我还将消息放入资源中,以允许本地化。
-
哪个例程更容易调试?哪个更容易维护?增加的复杂性是否足以让您受益?
-
嗯,最容易编写的代码是完全没有错误检查的代码。而且没有任何错误检查的代码也更容易阅读。
标签: c# exception-handling defensive-programming