Reputation: 13909
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
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
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
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
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