DataReader
действительно имеет поведение только для этой ситуации, CommandBehavior.CloseConnection
.
public SqlDataReader ExecuteDataReaderAsync(string connectionString, CommandType cmdType, string spName, params SqlParameter[] cmdParameters)
{
// These two are intentionally are not in a using statement, but it is ok, closing
// the reader cleans up the resources.
var conn = new SqlConnection(connectionString))
var cmd = new SqlCommand(spName, conn))
cmd.CommandType = cmdType;
cmd.Parameters.AddRange(cmdParameters);
conn.Open();
return cmd.ExecuteReader(CommandBehavior.CloseConnection);
}
При выбрасывайте DataReader
он закроет SqlConnection
, который является тем, что избавление от связи будет делать в любом случае (плюс поднять Disposed
событие).
Единственное утилизации SqlCommand
делает выпустить ссылку на внутреннюю переменную _cachedMetaData
, чтобы позволить ему получить GC'ed раньше и вызвать базу Dispose(bool)
на Component
, который только что он делает это поднять Disposed
событие.
До тех пор, пока вы не используете событие Disposed
, соединение или команду или соединение это решение должно работать на вас.
Если вы действительно должны «правильно» распоряжаться этими двумя, потому что вы полагаться на события, использовать трюк, похожий на то, что я сделал, когда I ran in to a similar situation в расположени CryptoStream
. Создайте оболочку, которая будет удалять ваше соединение и команду, когда читатель будет удален.
sealed class CleaningDataReader : IDataReader
{
private readonly IDataReader _reader;
private readonly IDisposable[] _itemsToDispose;
public CleaningDataReader(IDataReader reader, params IDisposable[] itemsToDispose)
{
if(reader == null)
throw new ArgumentNullException("reader");
_reader = reader;
_itemsToDispose = itemsToDispose;
}
public void Dispose()
{
_reader.Dispose();
if (_itemsToDispose != null)
{
foreach (var item in _itemsToDispose)
{
if(item != null)
item.Dispose();
}
}
}
public void Close()
{
_reader.Close();
}
public int Depth
{
get { return _reader.Depth; }
}
public int FieldCount
{
get { return _reader.FieldCount; }
}
public bool GetBoolean(int i)
{
return _reader.GetBoolean(i);
}
public byte GetByte(int i)
{
return _reader.GetByte(i);
}
public long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length)
{
return _reader.GetBytes(i, fieldOffset, buffer, bufferoffset, length);
}
public char GetChar(int i)
{
return _reader.GetChar(i);
}
public long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length)
{
return _reader.GetChars(i, fieldoffset, buffer, bufferoffset, length);
}
public IDataReader GetData(int i)
{
return _reader.GetData(i);
}
public string GetDataTypeName(int i)
{
return _reader.GetDataTypeName(i);
}
public DateTime GetDateTime(int i)
{
return _reader.GetDateTime(i);
}
public decimal GetDecimal(int i)
{
return _reader.GetDecimal(i);
}
public double GetDouble(int i)
{
return _reader.GetDouble(i);
}
public Type GetFieldType(int i)
{
return _reader.GetFieldType(i);
}
public float GetFloat(int i)
{
return _reader.GetFloat(i);
}
public Guid GetGuid(int i)
{
return _reader.GetGuid(i);
}
public short GetInt16(int i)
{
return _reader.GetInt16(i);
}
public int GetInt32(int i)
{
return _reader.GetInt32(i);
}
public long GetInt64(int i)
{
return _reader.GetInt64(i);
}
public string GetName(int i)
{
return _reader.GetName(i);
}
public int GetOrdinal(string name)
{
return _reader.GetOrdinal(name);
}
public DataTable GetSchemaTable()
{
return _reader.GetSchemaTable();
}
public string GetString(int i)
{
return _reader.GetString(i);
}
public object GetValue(int i)
{
return _reader.GetValue(i);
}
public int GetValues(object[] values)
{
return _reader.GetValues(values);
}
public bool IsClosed
{
get { return _reader.IsClosed; }
}
public bool IsDBNull(int i)
{
return _reader.IsDBNull(i);
}
public object this[int i]
{
get { return _reader[i]; }
}
public object this[string name]
{
get { return _reader[name]; }
}
public bool NextResult()
{
return _reader.NextResult();
}
public bool Read()
{
return _reader.Read();
}
public int RecordsAffected
{
get { return _reader.RecordsAffected; }
}
}
используется с
public IDataReader ExecuteDataReaderAsync(string connectionString, CommandType cmdType, string spName, params SqlParameter[] cmdParameters)
{
// These two are intentionally are not in a using statement, but it is ok, closing
// the reader cleans up the resources.
var conn = new SqlConnection(connectionString))
var cmd = new SqlCommand(spName, conn))
cmd.CommandType = cmdType;
cmd.Parameters.AddRange(cmdParameters);
conn.Open();
var reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
return new CleaningDataReader(reader, cmd, conn);
}
Я бы еще пойти с первым подходом, если вы действительно не нужна Disposed событие увольняют.
'rdr' зависит от SqlCommand (который зависит от SqlConnection). Но оба этих объекта удаляются перед тем, как выйти из метода ExecuteDataReaderAsync. – Ulric
@Ulric Как я могу решить проблему –
См. Ответ ниже :) – Ulric