2010-05-19 6 views
7

Мой метод, который вызывает SQL Server, возвращает DataReader, но из-за того, что мне нужно сделать, - который возвращает DataReader вызывающему методу, который находится в коде кода, - я не могу закрыть соединение в классе метод, который вызывает SQL-сервер. Из-за этого у меня нет окончательного или использования блоков.Должен ли я реализовать IDisposable здесь?

Правильный способ распоряжаться ресурсами, чтобы реализовать класс IDisposable? В качестве альтернативы я должен явным образом распоряжаться неуправляемым ресурсом (поля уровня класса) от вызывающего?

EDIT: Я пошлю DataReader назад, потому что мне нужно, чтобы связать конкретные данные из DataReader к регулятору ListItem, поэтому в классе вызова (Codebehind страница), я:

new ListItem(datareader["dc"]); (along those lines). 
+9

Почему вы хотите, чтобы отправить чтение данных на страницу? – Perpetualcoder

+0

Возвращение DataReader напрямую может быть плохой практикой, но в некоторых случаях это может быть полезно для него. – Venemo

+0

@Venemo - думаю, http://stackoverflow.com/questions/2867661/should-i-implement-idisposable-here/2869503#2869503 может служить ему лучше. – dss539

ответ

7

Я бы сказал, да , осуществить IDisposable. Одна из основных причин, по которой я могу это использовать, - это когда вы не можете доверять пользователю объекта достаточно, чтобы сделать это самостоятельно. Это, кажется, главный кандидат на это.

Это, однако, есть вопрос к вашей архитектуре. Почему вы хотите отправить сам DataReader на страницу вместо вызова метода для его выполнения (включая соответствующую очистку) путем возврата необходимого? Если необходимо предоставить реальному читателю страницу, то пусть будет так.

3

Да, вы должны реализовать IDisposable в своем пользовательском классе, если он содержит DataReader, который открыт при возврате на нижний уровень.

Это признанный образец при возвращении чего-то, который необходимо очистить.

2

Ваш класс

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 блок выходов.

+0

Зачем пытаться удалить их из переменной '_readers'? И установка '= null' на самом деле ничего не сделает. –

+0

Удаление их из _readers - отличный способ снять ссылки, в основном говоря GC, что он может их очистить. – Venemo

+0

'_readers = null' был просто по привычке. Это вам не нужно, потому что когда объект MyClass GC является GC, он будет пустым. Удаление их из объекта _readers уменьшает счетчик ссылок и поможет в сборке мусора, в конечном итоге GC поймет, что единственные ссылки на них - от ожидающих объектов для сбора, но это не может повредить. – Aren

3

Во-первых, передача 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(); 
    } 
} 
4

Удерживая соединение с базой данных как переменной-членом в классе читателя и делая класс чтения класса 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; 
     } 
    } 
} 
+0

...или, альтернативно, «общедоступный IEnumerable » с «читателем доходности»; и оставьте его вызывающему, как обрабатывать данные. – Joe

+0

В прошлом я делал очень похоже. Я обобщил его до класса, который просто инкапсулировал соединение и читателя вместе, - но вы можете их каскадировать (так что у вас все еще есть несколько читателей). Хорошо работает – philsquared

1

Общее правило заключается в том, что ваш класс должен реализовать IDisposable, если он непосредственно владеет неуправляемых ресурсов или содержит ссылку на другой объект IDisposable. Если ваш класс создает IDataReader в одном методе, но никогда не содержит эту ссылку, то вашему классу не нужно будет реализовывать IDisposable за правило (если только это не произойдет, чтобы удерживать IDisposable в стороне от IDataReader, созданного в этом методе).

Реальный вопрос, который вам нужно задать самому себе: должен ли ваш класс действительно держаться за этот IDataReader даже после того, как он доставил его вызывающему. Лично я считаю, что это плохой дизайн, потому что он размывает личную собственность. Кто на самом деле владеет IDisposable в этом случае? Кто несет ответственность за свою жизнь? Например, возьмите классы IDbCommand. Они создают IDataReader экземпляров и возвращают их вызывающим, но освобождают себя от владения. Это делает API чистым, а ответственность за управление жизненным циклом в этом случае недвусмысленна.

Независимо от проблемы собственности ваша конкретная ситуация требует реализации IDisposable; не потому, что ваш класс создает и возвращает экземпляр IDataReader, но поскольку он звучит так, будто он содержит объект IDbConnection.

2

Я ничего не верну. Вместо этого я передам делегата.

Например:

void FetchMeSomeReader(Action<IDataReader> useReader) 
{ 
    using(var reader = WhateverYouDoToMakeTheReader()) 
     useReader(reader); 
} 

Тогда в классе вызывающего:

void Whatever() 
{ 
    FetchMeSomeReader(SetFields); 
} 

void SetFields(IDataReader reader) 
{ 
    MyListItem = new ListItem(datareader["dc"]); 
} 
Смежные вопросы