我正在使用一个用于创建数据库连接的小例程:
前
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...
文档说,如果无法检索集合,调用ConnectionStrings会引发ConfigurationErrorException 。在这种情况下,我无能为力来处理这个异常,所以我会放手的。
下一部分是ConnectionStrings的实际索引以查找connectionName:
...ConnectionStrings[connectionName];
在这种情况下,ConnectionStrings 文档说如果找不到连接名称,该属性将返回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()
同样,文档没有说明如果无法创建连接会发生什么,但我可以再次检查是否有空返回对象:
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 方法是抽象的,因此由 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。
我还发现了两种可能存在空返回对象的情况;但我又一次只用一个例外换了另一个。
从积极的方面来说,它确实发现了两个问题,并解释了异常消息中发生的事情,而不是在路上发生的坏事(即责任止步于此)
但是这值得吗?这是矫枉过正吗?这种防御性编程是否出错了?