2010-05-19 99 views
7

我调用SQL Server的方法返回一个DataReader,但由于我需要做的是 - 返回DataReader到驻留在页面代码隐藏的调用方法 - 我无法关闭该类中的连接方法调用SQL服务器。由于这个原因,我没有最后或使用块。我应该在这里实施IDisposable吗?

配置资源的正确方式是使类实现IDisposable?或者,我应该显式地处理来自调用者的非托管资源(类级字段)?

编辑:我送DataReader的回来,因为我需要从DataReader按ListItem控件绑定特定的数据,所以在调用类(代码隐藏页),我做的:

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

你为什么要到数据读取器发送的网页? – Perpetualcoder 2010-05-19 16:57:14

+0

直接返回DataReader可能是不好的做法,但在某些情况下它可能对他有用。 – Venemo 2010-05-19 17:04:35

+0

@Venemo - 我认为http://stackoverflow.com/questions/2867661/should-i-implement-idisposable-here/2869503#2869503可能会更好地为他服务。 – dss539 2010-05-19 21:25:17

回答

7

我想说的是,执行IDisposable。据我所知,使用它的一个主要原因是当你不能相信对象的用户足够自己做到这一点时。这似乎是这方面的主要候选人。

然而,这是说,有一个问题,你的架构。为什么你想发送DataReader本身到页面而不是调用一个方法来为你做(包括相关的清理)通过返回什么是必要的?如果有必要让实际的读者阅读该页面,那就这样吧。

3

是的,如果您的自定义类包含返回到较低层时打开的DataReader,那么您应该在自定义类上实现IDisposable。

这是接受的模式,当返回的东西,需要清理。

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'实际上不会做任何事情。 – 2010-05-19 17:01:31

+0

从_readers中删除它们是取消引用它们的好方法,基本上告诉GC它可以清除它们。 – Venemo 2010-05-19 17:07:33

+0

'_readers = null'出于习惯。你不需要它,因为当'MyClass'对象被GC化时,空List也是如此。 从_readers对象中删除它们会减少引用计数并有助于垃圾收集,最终GC会意识到唯一对它们的引用来自待收集的对象,但它不会受到伤害。 – Aren 2010-05-19 17:10:34

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 2010-05-19 17:22:57

+0

我过去做得非常相似。我把它归结为一个只将连接和读者封装在一起的类 - 但是你可以级联它们(这样你仍然可以拥有多个阅读器)。工作得很好 – philsquared 2010-05-19 17:30:06

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"]); 
}