我有两个问题。使用声明问题
1)你应该总是在连接上使用using语句吗?那么,我会在连接上使用它,然后在连接中的另一个读取器上使用它?所以我会使用两个使用语句。
2)让我们假设你在连接上使用using语句,并且在连接上返回一个阅读器。所以你有两个使用语句。它是创建两个Try {}最后{}块还是仅一个?
谢谢!
我有两个问题。使用声明问题
1)你应该总是在连接上使用using语句吗?那么,我会在连接上使用它,然后在连接中的另一个读取器上使用它?所以我会使用两个使用语句。
2)让我们假设你在连接上使用using语句,并且在连接上返回一个阅读器。所以你有两个使用语句。它是创建两个Try {}最后{}块还是仅一个?
谢谢!
当对象实现IDisposable
时,应始终使用using
语句。这包括连接。
它会创建两个嵌套的try{}finally{}
块。
要回答每一个:
1)是的,这将尽快处理这两个最佳实践。
2)using()
将创建两个块,以相同的顺序彼此包裹。它将首先处理内部对象(阅读器),然后使用(连接)从外部处理对象。
请注意这里。你应该总是有任何使用声明实现IDisposable的本地对象。这不仅包括连接和读者,还包括命令。但它可能会有点棘手,有时确切地说,其中使用声明去。如果你不小心,可能会导致问题。例如,在使用声明之后的代码将在您使用之前关闭您的阅读器:
DataReader MyQuery()
{
string sql="some query";
using (var cn = new SqlConnection("connection string"))
using (var cmd = new SqlCommand(sql, cn))
{
cn.Open();
using (var rdr = cmd.ExecuteReader())
{
return rdr;
}
}
}
相反,您有四个选项。一是等待创建使用块,直到调用函数:
DataReader MyQuery()
{
string sql="some query";
using (var cn = new SqlConnection("connection string"))
using (var cmd = new SqlCommand(sql, cn))
{
cn.Open();
return cmd.ExecuteReader();
}
}
using (var rdr = MyQuery())
{
while (rdr.Read())
{
//...
}
}
当然,你还是要小心你的连接存在,这意味着记得到处使用的功能,写一个使用块。
选项二只是处理方法本身的查询结果,但是会破坏数据层与程序其余部分的分离。第三种选择是让你的MyQuery()函数接受一个你可以在while(rdr.Read())循环中调用的Action类型的参数,但这只是尴尬。
我一般喜欢选择四:把数据读取到一个IEnumerable,像这样:
IEnumerable<IDataRecord> MyQuery()
{
string sql="some query";
using (var cn = new SqlConnection("connection string"))
using (var cmd = new SqlCommand(sql, cn))
{
cn.Open();
using (var rdr = cmd.ExecuteReader())
{
while (rdr.Read())
yield return rdr;
}
}
}
现在一切都将被正确关闭,处理它的代码是在同一个地方。您还可以获得不错的奖励:您的查询结果可以与任何linq运算符一起使用。
最后,新的东西,我玩我下次去打造,结合了IEnumerable与传递一个参数代表一个全新的项目:
//part of the data layer
private static IEnumerable<IDataRecord> Retrieve(string sql, Action<SqlParameterCollection> addParameters)
{
//DL.ConnectionString is a private static property in the data layer
// depending on the project needs, it can be implementing to read from a config file or elsewhere
using (var cn = new SqlConnection(DL.ConnectionString))
using (var cmd = new SqlCommand(sql, cn))
{
addParameters(cmd.Parameters);
cn.Open();
using (var rdr = cmd.ExecuteReader())
{
while (rdr.Read())
yield return rdr;
}
}
}
然后我就用它这样的数据层中:
public IEnumerable<IDataRecord> GetFooChildrenByParentID(int ParentID)
{
//I could easily use a stored procedure name instead, and provide overloads for commandtypes.
return Retrieve(
"SELECT c.*
FROM [ParentTable] p
INNER JOIN [ChildTable] c ON c.ParentID = f.ID
WHERE f.ID= @ParentID", p =>
{
p.Add("@ParentID", SqlDbType.Int).Value = ParentID;
}
);
}
“您应该始终在任何实现IDisposable的对象上使用语句”:这不完全正确......您需要为实现IDisposable的**局部变量**,而不是类成员执行此操作,因为它们会通常会在班级的其他地方重用。但是,在这种情况下,类本身应该实现IDisposable,并将Dispose方法中的所有IDisposable成员置于其中Dispose方法 – 2010-04-03 16:31:14
请注意,在using或try/finally块内执行“yield return”的类将实现IDisposable,并且无法正确处置他们将导致使用 - 处置或最终代码不被执行。 – supercat 2011-03-23 19:07:09
1)如果你总是使用使用 语句连接上?所以,我会在 连接 中使用它的连接,然后 一个又一个的读卡器?所以我会用两个 using语句。
是的,因为他们实施IDisposable
。而且不要忘了在命令using
声明过:
using (DbConnection connection = GetConnection())
using (DbCommand command = connection.CreateCommand())
{
command.CommandText = "SELECT FOO, BAR FROM BAZ";
connection.Open();
using (DbDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
....
}
}
}
2)假设你使用使用 语句的连接上也正在 连接上返回的 读者。所以,你有两个使用 语句。它创建两个 尝试{}最后{}块或只是一个?
每个using
语句将创建自己的try/finally
块
1特点)。你需要特别地避免这个技术当在asynchronous ADO.NET methods中使用连接 - 就像BeginExecuteReader一样,因为很可能你会掉到范围之外并尝试在异步操作仍在进行时处置连接。这与使用类变量而不是局部变量的情况类似。很多时候,连接参考被存储在用作“控制块”用于异步操作的类。
你不应该_always_使用'using'声明 - 哪里可能出现问题的一个例子是调用' Command.ExecuteReader'时。这种方法可能会抛出异常,如果是这样,它们将不会被正确处理,并且软件将会失败。更好地处理可能的异常并手动调用Dispose()或其等价物。 –
Conrad
2012-03-15 17:01:49