Dapper 的嵌套“using”子句 - 说明?
Dapper's nested `using` clause - Clarification?
我正在了解 Dapper 是如何在幕后工作的。
但是我看到了这种处理方式,我不明白。
大体上—— this 是 QueryAsync
的实现方式:
/*1*/ public async Task<IEnumerable<T>> QueryAsync<T>(string sql, Func<IDataRecord, T> projector, DbConnection _conn, dynamic param = null)
/*2*/ {
/*3*/
/*4*/ DbDataReader reader = null;
/*5*/ bool wasClosed = _conn.State == ConnectionState.Closed;
/*6*/ try
/*7*/ {
/*8*/
/*9*/ using (var cmd = _conn.CreateCommand())
/*10*/ {
/*11*/ if (param!=null)
/*12*/ foreach (var prop in param.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
/*13*/ {
/*14*/ var parameter = cmd.CreateParameter();
/*15*/ parameter.ParameterName = prop.Name;
/*16*/ parameter.Value = prop.GetValue(param, null);
/*17*/ cmd.Parameters.Add(parameter);
/*18*/ }
/*19*/
/*20*/ await _conn.OpenAsync().ConfigureAwait(false);
/*21*/ cmd.CommandTimeout = 100000;
/*22*/ cmd.CommandText = sql;
/*23*/ reader = await cmd.ExecuteReaderAsync().ConfigureAwait(false);
/*24*/ List<T> buffer = new List<T>();
/*25*/ while (await reader.ReadAsync().ConfigureAwait(false)) buffer.Add(projector(reader));
/*26*/ return buffer;
/*27*/ }
/*28*/
/*29*/ }
/*30*/ finally
/*31*/ {
/*32*/ using (reader) { }
/*33*/ if (wasClosed) _conn.Close();
/*34*/ }
/*35*/ }
我能理解他为什么不在连接上使用 using
,那是因为他想通过 wasClosed 变量 有条件地 关闭连接.
为此 - 他必须使用 try/finally
子句。 (因此条件关闭将在 finally
子句中)
但我的问题是关于第 32 行的。
与其做 using at the finally clause ,他还可以做:
using (DbDataReader reader = await cmd.ExecuteReaderAsync().ConfigureAwait(false))
{
List<T> buffer = new List<T>();
while (await reader.ReadAsync().ConfigureAwait(false)) buffer.Add(projector(reader));
return buffer;
}
所以 finally
子句剩下:
finally
{
//using (reader) { } //removed
if (wasClosed) _conn.Close();
}
问题
我在 dapper 的 finally 子句中多次看到这个 using
子句。
我一定在这里遗漏了什么,但是这个模式实现了什么我的建议没有实现?
我不是@MarcGravell,但我认为您遗漏了一件事。您粘贴的代码与您引用的 link 不完全匹配。相关代码路径如下所示:
try
{
if (command.Buffered)
{
List<T> buffer = new List<T>();
while (await reader.ReadAsync(cancel).ConfigureAwait(false))
{
buffer.Add((T)func(reader));
}
while (await reader.NextResultAsync().ConfigureAwait(false)) { }
command.OnCompleted();
return buffer;
}
else
{
// can't use ReadAsync / cancellation; but this will have to do
wasClosed = false; // don't close if handing back an open reader;
// rely on the command-behavior.
var deferred = ExecuteReaderSync<T>(reader, func, command.Parameters);
reader = null; // to prevent it being disposed before the caller gets to see it
return deferred;
}
}
finally
{
using (reader) { } // dispose if non-null
if (wasClosed) cnn.Close();
}
该方法可以是 return 缓冲结果(由 command.Buffered
标志指示)或延迟迭代器。如果 Marc 用 using
语句和 return 迭代器包装 reader,它(reader)将在调用站点执行它时被处理掉.通过将 reader 设置为 null
(在他 return 的延迟结果之前的行中)他阻止了 reader 被释放,因为在 finally 块中使用会翻译成这样:
finally
{
IDisposable disposable = reader;
try
{
}
finally
{
if (dispoable != null)
{
disposable.Dispose();
}
}
}
当他将reader设置为null
时,它没有被释放,迭代器中存在的引用仍然存在,指向reader。这样,他既可以在正常代码路径中处理 reader,但如果请求延迟迭代器,则可以保持它的活动状态。
我正在了解 Dapper 是如何在幕后工作的。
但是我看到了这种处理方式,我不明白。
大体上—— this 是 QueryAsync
的实现方式:
/*1*/ public async Task<IEnumerable<T>> QueryAsync<T>(string sql, Func<IDataRecord, T> projector, DbConnection _conn, dynamic param = null)
/*2*/ {
/*3*/
/*4*/ DbDataReader reader = null;
/*5*/ bool wasClosed = _conn.State == ConnectionState.Closed;
/*6*/ try
/*7*/ {
/*8*/
/*9*/ using (var cmd = _conn.CreateCommand())
/*10*/ {
/*11*/ if (param!=null)
/*12*/ foreach (var prop in param.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
/*13*/ {
/*14*/ var parameter = cmd.CreateParameter();
/*15*/ parameter.ParameterName = prop.Name;
/*16*/ parameter.Value = prop.GetValue(param, null);
/*17*/ cmd.Parameters.Add(parameter);
/*18*/ }
/*19*/
/*20*/ await _conn.OpenAsync().ConfigureAwait(false);
/*21*/ cmd.CommandTimeout = 100000;
/*22*/ cmd.CommandText = sql;
/*23*/ reader = await cmd.ExecuteReaderAsync().ConfigureAwait(false);
/*24*/ List<T> buffer = new List<T>();
/*25*/ while (await reader.ReadAsync().ConfigureAwait(false)) buffer.Add(projector(reader));
/*26*/ return buffer;
/*27*/ }
/*28*/
/*29*/ }
/*30*/ finally
/*31*/ {
/*32*/ using (reader) { }
/*33*/ if (wasClosed) _conn.Close();
/*34*/ }
/*35*/ }
我能理解他为什么不在连接上使用 using
,那是因为他想通过 wasClosed 变量 有条件地 关闭连接.
为此 - 他必须使用 try/finally
子句。 (因此条件关闭将在 finally
子句中)
但我的问题是关于第 32 行的。
与其做 using at the finally clause ,他还可以做:
using (DbDataReader reader = await cmd.ExecuteReaderAsync().ConfigureAwait(false))
{
List<T> buffer = new List<T>();
while (await reader.ReadAsync().ConfigureAwait(false)) buffer.Add(projector(reader));
return buffer;
}
所以 finally
子句剩下:
finally
{
//using (reader) { } //removed
if (wasClosed) _conn.Close();
}
问题
我在 dapper 的 finally 子句中多次看到这个 using
子句。
我一定在这里遗漏了什么,但是这个模式实现了什么我的建议没有实现?
我不是@MarcGravell,但我认为您遗漏了一件事。您粘贴的代码与您引用的 link 不完全匹配。相关代码路径如下所示:
try
{
if (command.Buffered)
{
List<T> buffer = new List<T>();
while (await reader.ReadAsync(cancel).ConfigureAwait(false))
{
buffer.Add((T)func(reader));
}
while (await reader.NextResultAsync().ConfigureAwait(false)) { }
command.OnCompleted();
return buffer;
}
else
{
// can't use ReadAsync / cancellation; but this will have to do
wasClosed = false; // don't close if handing back an open reader;
// rely on the command-behavior.
var deferred = ExecuteReaderSync<T>(reader, func, command.Parameters);
reader = null; // to prevent it being disposed before the caller gets to see it
return deferred;
}
}
finally
{
using (reader) { } // dispose if non-null
if (wasClosed) cnn.Close();
}
该方法可以是 return 缓冲结果(由 command.Buffered
标志指示)或延迟迭代器。如果 Marc 用 using
语句和 return 迭代器包装 reader,它(reader)将在调用站点执行它时被处理掉.通过将 reader 设置为 null
(在他 return 的延迟结果之前的行中)他阻止了 reader 被释放,因为在 finally 块中使用会翻译成这样:
finally
{
IDisposable disposable = reader;
try
{
}
finally
{
if (dispoable != null)
{
disposable.Dispose();
}
}
}
当他将reader设置为null
时,它没有被释放,迭代器中存在的引用仍然存在,指向reader。这样,他既可以在正常代码路径中处理 reader,但如果请求延迟迭代器,则可以保持它的活动状态。