好的,我搜索过“类似”的主题,但仍然没有找到我在这里要问的任何答案。
我有一个在 System.Data.SqlClient 命名空间下创建多个sql对象的函数。我已经读过 使用 语句在 使用 块之后处理了一个对象,但声明的变量是readonly 。我的函数重用了其中一些变量,因此我无法在 中使用 语句声明它们。
为清晰起见,这是我的功能正文。我应该在其他对象(命令,事务,阅读器等)上调用Dispose,还是 使用 通过连接对象递归处理它们?我该如何处置这些物品?
我还是C#的新手(我来自C / C ++背景)所以如果这个问题听起来很无知,请原谅我。
public string SignIn(string userId, string password)
{
SqlCommand sqlCommand = null;
SqlTransaction sqlTransaction = null;
string sessionId = "";
using(SqlConnection sqlConnection = new SqlConnection Properties.Settings.Default.SessionManagerDBConnectionString))
{
try
{
sqlConnection.Open();
sqlCommand = sqlConnection.CreateCommand();
sqlCommand.CommandText = "GetUserByUserIdPassword";
sqlCommand.CommandTimeout = 30;
sqlCommand.CommandType = CommandType.StoredProcedure;
SqlParameter parameterUserId = sqlCommand.Parameters.Add("@UserId", SqlDbType.NVarChar, 32);
parameterUserId.Value = userId;
SqlParameter parameterPassword = sqlCommand.Parameters.Add("@Password", SqlDbType.NChar, 64);
parameterPassword.Value = this.GetSHA256Hash(password);
sqlTransaction = sqlConnection.BeginTransaction("SampleTransaction");
// more database activity, execute command, store results in datareader
sqlTransaction.Commit();
sqlConnection.Close();
}
catch (SqlException ex)
{
if(sqlTransaction != null)
sqlTransaction.Rollback();
MessageBox.Show(ex.Number + ":" + ex.Message, ex.Server + ":" + ex.Source, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
return sessionId;
}
我试图再次搜索类似的问题并找到一些更接近的答案。
Is SqlCommand.Dispose() required if associated SqlConnection will be disposed?
Does SqlCommand.Dispose close the connection?
我想我应该在try-catch中添加一个finally子句,并为我创建的所有sql对象调用几个Dispose方法。我希望这样做还是有推荐的风格吗?
finally
{
if(sqlCommand != null)
sqlCommand.Dispose();
if(sqlTransaction != null)
sqlTransaction.Dispose();
...
}
我尝试在try-catch块中为其中一个sqlCommand对象放置一个using语句,但如果在抛出异常时该部分代码中止,则执行会跳转到catch部分。使用不会处置sqlCommand对象。
try
{
...
using(sqlCommand = sqlConnection.CreateCommand())
{
sqlCommand.CommandText = "GetUserByUserIdPassword2";
sqlCommand.CommandTimeout = 30;
sqlCommand.CommandType = CommandType.StoredProcedure;
SqlParameter parameterUserId = sqlCommand.Parameters.Add("@UserId", SqlDbType.NVarChar, 32);
parameterUserId.Value = userId;
SqlParameter parameterPassword = sqlCommand.Parameters.Add("@Password", SqlDbType.NChar, 64);
parameterPassword.Value = this.GetSHA256Hash(password);
SqlDataReader reader = sqlCommand.ExecuteReader(CommandBehavior.SingleRow);
// throws exception, no stored procedure "GetUserByUserIdPassword2"
}
...
}
catch() {}
// sqlCommand still accessible at this point because using above was "aborted".
答案 0 :(得分:2)
如果对象实现IDisposable
,则使用using
。如果由于某种原因需要创建新的SqlCommand
,请完成一个using
块并启动一个新块。如果您仍然需要访问第一个SqlCommand
,请嵌套它们
只要您没有从命令中打开SqlCommand
,就可以重用datareader
个对象。因此,您可以创建SqlCommand
,设置其所有属性并执行它,然后重置其所有属性并再次执行,依此类推。这确实略微节省了内存分配的成本,但我认为这也降低了代码的清晰度,所以只有在分析证明有必要时才会这样做。
答案 1 :(得分:1)
将通过连接对象递归处理它们
当然,using
对SqlConnections
或其他ADO.NET对象一无所知。它只知道打电话给Dispose
。无论处置的对象是什么,都会发生。
恰好是处理SqlConnection
也会处理与连接相关的所有读者和命令的资源。您只需要处理连接。
我不知道这是否记录在MSDN上,但我知道它是通过反编译程序集来实现的。出于兼容性原因,他们永远不能改变这种行为,因此可以安全地依赖它。
通常,您必须在实现IDisposable
的任何对象上调用dispose。此规则的例外情况是,您确定不会调用Dispose
是安全的。就像在这种情况下一样。
答案 2 :(得分:1)
我想我找到了答案!
即使抛出的异常跳过了嵌套的using语句,sqlCommand对象仍可在代码的底部访问,但sqlCommand仍会在稍后处理。我通过实际为所述sqlCommand对象的dispos事件分配一个函数来测试它。
由于事务对象的要求,此处的代码与上面的代码略有不同。但逻辑基本相同。
try
{
using(sqlCommand = new SqlCommand("GetUserByUserIdPassword2", sqlConnection, sqlTransaction))
{
sqlCommand.CommandTimeout = 15;
sqlCommand.CommandType = CommandType.StoredProcedure;
SqlParameter parameterUserId = sqlCommand.Parameters.Add("@UserId", SqlDbType.NVarChar, 32);
parameterUserId.Value = userId;
SqlParameter parameterPassword = sqlCommand.Parameters.Add("@Password", SqlDbType.NChar, 64);
parameterPassword.Value = this.GetSHA256Hash(password);
sqlCommand.Disposed += new System.EventHandler(this.sqlCommand_Disposed);
SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(CommandBehavior.SingleRow);
// exception thrown, no sored proc "GetUserByUserIdPassword2"
sqlDataReader.Close();
}
}
catch(...) {}
...
private void sqlCommand_Disposed(object sender, EventArgs e)
{
MessageBox.Show("sqlCommand has been disposed");
}
所以,基本上,即使嵌套的using语句是" aborted"通过try块中抛出的异常,并且执行被跳过到catch块,在函数退出之后(或当变量超出范围时)仍然为该对象调用Dispose方法。
我认为这种行为对于任何数量的嵌套使用语句都是相同的。
答案 3 :(得分:-1)
在CSharp中,using
是一个特殊关键字,在代码中有不同的行为。
如果您将using
放在代码页的顶部,然后使用act作为c ++' s Include
或VB(和其他一些脚本langs')Import
如果你写了一个" using
"然后它就像一个paranthesis,它的作用就像是CSharp的Dispose()
方法的别名..或c ++的Destructor tilda
或普通c的free()
方法。
关于SQL Connection
..最好的方法是首先Close()
连接(所以这个,连接仍然活着 - 实际上只是准备使用 - 但是null ..返回等待连接池中的新订单..)然后处理,如果你需要..
如果您仍有问题,并且怀疑调用Dispose()
方法不足以释放资源
然后您可以使用Garbage Collector
' Collect()
静态方法来强制进行垃圾回收
但是,由于面临意外的垃圾收集问题而不是微软推荐(即您收集表单但需要从该表单中获取一些变量)
在使用示例中:
public void Dispose (bool ForceToCollect )
{
if (ForceToCollect)
GC.Collect (0, GCCollectionMode.Forced );
}
在Gc.Collect()
方法中,您可以设置使用第一个选项释放哪个数据内存组,其中int为int ..如果您计划释放最后一个组的资源 - 表示上次创建的项目组,作为最后创建的示例表单实例及其所有子控件 - 然后int应为0;在最后一个创建之前为1的那个应该是1,依此类推..
希望这会有所帮助..