是否需要以下结构?
using (Something something = new Something())
{
try
{
}
finally
{
something.SomeCleanup();
}
}
或者,所有清理任务是否应在隐式something.Dispose()
调用中执行?
以下是有问题的代码:
public static DataTable GetDataTable(string cmdText, IEnumerable<Parameter> parameters)
{
// Create an empty memory table.
DataTable dataTable = new DataTable();
// Open a connection to the database.
using (SqlConnection connection = new SqlConnection(ConfigurationTool.ConnectionString))
{
connection.Open();
// Specify the stored procedure call and its parameters.
using (SqlCommand command = new SqlCommand(cmdText, connection))
{
command.CommandType = CommandType.StoredProcedure;
SqlParameterCollection parameterCollection = command.Parameters;
foreach (Parameter parameter in parameters)
parameterCollection.Add(parameter.SqlParameter);
try
{
// Execute the stored procedure and retrieve the results in the table.
using (SqlDataAdapter dataAdapter = new SqlDataAdapter(command))
try
{
dataAdapter.Fill(dataTable);
}
catch
{
dataTable.Dispose();
dataTable = null;
}
}
finally
{
//parameterCollection.Clear();
}
}
}
return dataTable;
}
注意:我已定义了Parameter
类,因此此函数的用户无需直接处理SqlParameter
的创建。 SqlParameter
类的Parameter
属性可用于检索SqlParameter
。
在某些时候,我的程序执行以下操作(无法发布代码,因为它涉及很多类;基本上,我有一个创建大量对象的迷你框架):
Parameter
s。GetDataTable('sp_one', parameters)
。GetDataTable('sp_two', parameters)
。答案 0 :(得分:5)
using
关键字仅调用.Dispose()
方法。如果您在IDisposable对象上的dispose方法之外进行必要的清理,那么您需要在它自己的finally块中执行此操作。这提出了两点:
using
块。这是一个很好的习惯,总是为你的IDisposable实例提供一个。根据您发布的代码,问题是您的参数仍然存在于某个地方(也许您正在重新使用它们?)。由于参数仍然是有根的,因此无法收集。它们还包含对它们所附加命令的引用,因此您的SqlCommand对象也不能立即收集,因为它现在仍然是根的。
关键是.Net框架为 unamanaged 资源保留Dispose()模式。由于SqlParameters和SqlParameterCollection是托管类型,因此在收集它们之前不会触及它们,这与处理完全分开。最后收集SqlCommand时,它的SqlParameter集合也会被处理掉。只是不要混淆收集,处置和他们的目的。
您要做的是在添加每个参数时复制每个参数,而不是将现有参数添加到集合中。
public static DataTable GetDataTable(string cmdText, IEnumerable<Parameter> parameters)
{
// Create an empty memory table.
DataTable dataTable = new DataTable();
// Prepare a connection to the database and command to execute.
using (SqlConnection connection = new SqlConnection(ConfigurationTool.ConnectionString))
using (SqlCommand command = new SqlCommand(cmdText, connection))
{
command.CommandType = CommandType.StoredProcedure;
SqlParameterCollection parameterCollection = command.Parameters;
foreach (Parameter parameter in parameters)
parameterCollection.Add(CloneParameter(parameter.SqlParameter));
// Execute the stored procedure and retrieve the results in the table.
using (SqlDataAdapter dataAdapter = new SqlDataAdapter(command))
{
dataAdapter.Fill(dataTable);
}
}
return dataTable;
}
有些事情需要注意:我能够摆脱所有你的尝试块。不需要其中一个。此外,SqlDataAdapter.Fill()方法将为您打开和关闭连接,因此您不需要该部分。
现在让我们来谈谈CloneParameter()函数。我得到的印象是你觉得它违背了代码的目的,即尝试重用参数。我保证你在这里重复使用参数是一个坏主意。性能损失可以忽略不计,特别是与存储过程执行相比。我将CloneParameter()实现留给了你,原因有二:首先它是微不足道的,其次是我们已经超出了我的正常数据访问模式。我通常做的添加参数是接受Action&lt; SqlParameterCollection&gt;委托,而不是可枚举的参数。声明的函数更像是这样:
public IEnumerable<IDataRecord>GetData(string cmdText, Action<SqlParameterCollection> addParameters)
并且被称为:
foreach(var record in GetData("myprocedurename", p =>
{
p.Add( /*new parameter here*/ );
p.Add( /*new parameter here*/ );
//...
})
.Select( /*Returning a IEnumerable rather than a datatable allows me to use it with linq to objects.*/
/* For example, you could use this spot to convert from DataRecords returned by ADO.Net to business objects */
))
{
// use the results here...
}
由于你连续填写两个表,听起来你有一些工作要做客户端,这可能证明与DataReader / IEnumerable方法相比,但我确实想提到这一点,因为大部分时间都是基于您在DataReader上的代码是更好的选择。
对于我现有的基于Action-delegate的模式,我希望尽可能多地重复使用一组重复的参数,我会做一个真实的命名方法,知道如何添加参数和匹配我的行动代表。然后我可以直接传递该方法,并重新使用所需的参数。
答案 1 :(得分:4)
有趣的问题!
这一切都取决于你的Something
课程。如果它的设计很差并且需要进行多阶段清理,它就会强迫它的客户特质。
你不应该设计类似的那样。如果要进行临时清理,请将它们封装在自己的类中,并使用如下代码:
using (Something something = new Something()) {
// ...
using (SomethingElse somethingElse = something.GiveMeSomethingElse()) {
}
// ...
}
<强>更新强>
对于您的示例,它可能如下所示:
using (SqlConnection connection = new SqlConnection(connectionString)) {
connection.Open();
using (SqlCommand command = new SqlCommand("select * from MyTable where id = @id", connection)) {
// to "reuse" the parameters collection population, just extract this to a separate method
command.Parameters.Add(new SqlParameter("id", id));
// ... execute the command
}
}
更新2:
这样做:
GetDataTable('sp_one', CreateParameters());
GetDataTable('sp_two', CreateParameters());
答案 2 :(得分:1)
Dispose应该清理所有非托管资源。例如,使用另一种清理方法来执行某些功能或数据库操作是完全可能的。