Мой метод, который называет возвраты SQL Server a DataReader
но из-за того, что я должен сделать - который является возвратом DataReader
к вызывающему методу, который находится в коде страницы - позади - я не могу закрыть соединение в классе метода, который называет SQL-сервер. Из-за этого, я имею не наконец или блоки использования.
Корректный способ расположить ресурсы для создания реализации класса IDisposable
? Кроме того, я должен явно расположить неуправляемый ресурс (поля уровня класса) от вызывающей стороны?
Править: Я передаю datareader обратно, потому что я должен связать определенные данные от datareader до управления listitem, таким образом, в классе вызова (страница Codebehind), я делаю:
new ListItem(datareader["dc"]); (along those lines).
Я бы сказал, да, реализуйте IDisposable
. Насколько я могу судить, одна из основных причин его использования заключается в том, что вы не можете доверять пользователю объекта достаточно, чтобы сделать это самостоятельно. Кажется, это главный кандидат на это.
Однако при этом возникает вопрос к вашей архитектуре. Почему вы хотите отправить на страницу сам DataReader
, а не вызывать метод, который сделает это за вас (включая соответствующую очистку), возвращая то, что необходимо? Если необходимо выдать на страницу реального читателя, пусть так и будет.
Да, вы должны реализовать IDisposable в вашем пользовательском классе, если он содержит DataReader, который открыт при возврате на нижний уровень.
Это принятый шаблон при возврате чего-то, что нужно очистить.
Во-первых, передача DataReader
может быть не совсем тем, что вы хотите сделать, но я предполагаю, что это так.
Правильный способ справиться с этим - передать обратно составной тип, который либо инкапсулирует, либо предоставляет DataReader
и удерживает соединение, а затем реализовать IDisposable
для этого типа. Утилизируя этот тип, утилизируйте как считыватель, так и соединение.
public class YourClass : IDisposable
{
private IDbConnection connection;
private IDataReader reader;
public IDataReader Reader { get { return reader; } }
public YourClass(IDbConnection connection, IDataReader reader)
{
this.connection = connection;
this.reader = reader;
}
public void Dispose()
{
reader.Dispose();
connection.Dispose();
}
}
Ваш класс
class MyClass : IDisposable
{
protected List<DataReader> _readers = new List<DataReader>();
public DataReader MyFunc()
{
///... code to do stuff
_readers.Add(myReader);
return myReader;
}
private void Dispose()
{
for (int i = _readers.Count - 1; i >= 0; i--)
{
DataReader dr = _reader.Remove(i);
dr.Dispose();
}
_readers = null;
// Dispose / Close Connection
}
}
Затем вне вашего класса
public void FunctionThatUsesMyClass()
{
using(MyClass c = new MyClass())
{
DataReader dr = c.MyFunc();
}
}
Все читатели и экземпляр MyClass
очищаются при выходе из блока using
.
Я бы ничего не вернул. Вместо этого я бы пропустил делегата.
Например:
void FetchMeSomeReader(Action<IDataReader> useReader)
{
using(var reader = WhateverYouDoToMakeTheReader())
useReader(reader);
}
Затем в вызывающем классе:
void Whatever()
{
FetchMeSomeReader(SetFields);
}
void SetFields(IDataReader reader)
{
MyListItem = new ListItem(datareader["dc"]);
}
Общее правило заключается в том, что класс должен реализовать IDisposable
, если он непосредственно содержит неуправляемые ресурсы или содержит ссылку на другой объект IDisposable
. Если класс создает IDataReader
в одном методе, но никогда не содержит эту ссылку, то вашему классу не нужно будет реализовывать IDisposable
в соответствии с правилом (если только он не содержит IDisposable
помимо IDataReader
, созданного в этом методе).
Реальный вопрос, который вам нужно задать себе, заключается в том, действительно ли ваш класс должен удерживать этот IDataReader
даже после того, как он доставил его вызывающему абоненту. Лично я думаю, что это плохой дизайн, потому что он размывает линию собственности. Кто на самом деле владеет IDisposable
в этом случае? Кто несет ответственность за его срок службы?Возьмем, к примеру, классы IDbCommand
. Они создают экземпляры IDataReader
и возвращают их вызывающим абонентам, но освобождают себя от права собственности. Это делает API чистым, и ответственность за управление жизненным времени в этом случае однозначна.
Независимо от вопроса о собственности, ваша конкретная ситуация требует внедрения IDisposable; не потому, что класс создает и возвращает экземпляр IDataReader
, а потому, что он звучит так, как будто он содержит объект IDbConnection
.
Держать соединение с базой данных в качестве переменной-члена в вашем классе читателя и заставить ваш класс читателя реализовать IDisposable кажется мне нормальным.
Однако, вы могли бы рассмотреть возможность сделать ваш метод возвращающим IEnumerable и использовать yield return
операторы для прохождения через читателя данных. Таким образом, вы сможете возвращать результаты и продолжать очистку внутри метода.
Вот примерный набросок того, что я имею в виду:
public IEnumerable<Person> ReadPeople(string name)
{
using (var reader = OpenReader(...))
{
// loop through the reader and create Person objects
for ...
{
var person = new Person();
...
yield return person;
}
}
}