Andrew Harry
Andrew Harry

Reputation: 13909

Wrapping a Non-inheritable class in C#

I have a simple problem.

I want to decorate the SqlDataReader class so that when the dispose or close methods are called I can dispose of a hidden resource at the same time.

the SqlDataReader class is not inheritable.

How can I accomplish this? I really don't want to implement the DbDataReader, IDataReader, IDisposable & IDataRecord interfaces

Upvotes: 6

Views: 2536

Answers (4)

Dave Black
Dave Black

Reputation: 8019

You can use the Decorator pattern to wrap an instance of SqlDataReader. You would have to implement the public "interface" of it though and use your Decorator in place of the SqlDataReader. You can just forward the method calls on your Decorator to the wrapped SqlDataReader. To go this route you would need to be using interfaces (with your own implementation) that provides the database command, etc. I had to do something similar for mocking my database implementation for unit testing. It is a little work but it can be done.

Upvotes: 0

Josh
Josh

Reputation: 69262

Even if you could inherit from SqlDataReader it wouldn't matter anyway because you couldn't make SqlCommand create an instance of your derived class.

Implementing IDataReader in a wrapper is really not hard at all when you're just deferring to the underlying SqlDataReader. It's just a little time consuming but not that bad.

But I'm curious, is the resource you want disposed the connection? If so there is a CloseConnection member of the CommandBehavior enum that ensures the connection will be closed when the data reader is closed.

var reader = command.ExecuteReader(CommandBehavior.CloseConnection);
...
reader.Close(); // also closes connection

Note that Close/Dispose are the same thing on SqlDataReader.

Finally, here's one last suggestion that has served me well in the past. Note that in the following loose example, you own the SqlDataReader from start to finish even though you are "yielding" back to the caller at each record.

private static IEnumerable<IDataRecord> GetResults(this SqlCommand command) {
    using (var myTicket = new MyTicket())
    using (var reader = command.ExecuteReader()) {
        while (reader.Read()) {
            yield return reader;
        }
    }
    // the two resources in the using blocks above will be
    // disposed when the foreach loop below exits
}

...

foreach (var record in myCommand.GetResults()) {

    Console.WriteLine(record.GetString(0));

}

// when the foreach loop above completes, the compiler-generated
// iterator is disposed, allowing the using blocks inside the
// above method to clean up the reader/myTicket objects

Upvotes: 6

Scott Chamberlain
Scott Chamberlain

Reputation: 127553

http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldatareader.aspx The class is is not sealed. You should just be able to call base.dispose() at the start of your override and then put your code after.

I dont have my IDE in front of me but it should look somthing like

public myClass : SqlDataReader
{
    protected overide void Dispose(bool disposing) : Base(disposing)
    {
        myCleanupCode();
    }
    protected overide void Dispose()
    {
        myCleanupCode();
    }
    private myCleanupCode()
    {
        //Do cleanup here so you can make one change that will apply to both cases.
    }
}

EDIT--- just read the orginal comments, i see that it has the private constructor, let me break out my VS2008 and ill brb

looking in to it, and everyone is trying these fancy solutions, the only thing I see that can be done is

public class myClass : IDisposable
{

    public SqlDataReader dataReader { get; set; }

    #region IDisposable Members

    public void Dispose()
    {
        dataReader.Dispose();
        //My dispose code
    }

    #endregion
}

EDIT--- Sigh, this is excatly what Silky posted 40 min ago.

Upvotes: 1

Noon Silk
Noon Silk

Reputation: 55082

Reverse it; use your "Hidden" resource as the main thing, implement IDisposable, and then close the DataReader when you're done with it.

Upvotes: 3

Related Questions