2010-01-05 3 views
6

J'ai un problème simple.Envelopper une classe non héritable en C#

Je veux décorer la classe SqlDataReader de sorte que lorsque les méthodes disposer ou fermer sont appelées, je peux disposer d'une ressource cachée en même temps.

La classe SqlDataReader n'est pas héritable.

Comment puis-je accomplir ceci? Je ne veux vraiment pas implémenter le DbDataReader, IDataReader, IDisposable & interfaces IDataRecord

+0

Que voulez-vous dire non héritable? Est-ce une classe scellée? Pourriez-vous élaborer sur les interfaces? N'avez-vous pas à les mettre en œuvre? Aussi, pourquoi ne veux-tu pas? –

+0

SqlDataReader n'est pas scellé donc il est héritable, mais ses constructeurs sont privés donc vous ne pouvez pas satisfaire le constructeur de base quand vous en héritez - je ne fais que copier des détails mais pourtant, vous avez raison. –

+0

En plus de mon commentaire sur la réponse de Silky, je ne peux pas m'empêcher de me demander ... qu'est-ce que vous pourriez avoir besoin d'empaqueter avec 'SqlDataReader'? J'espère vraiment que ce n'est pas un 'SqlCommand' ou' SqlConnection' ... – Aaronaught

Répondre

5

Même si vous pouviez hériter de SqlDataReader, cela n'aurait aucune importance car vous ne pourriez pas faire en sorte que SqlCommand crée une instance de votre classe dérivée.

L'implémentation d'IDataReader dans un wrapper n'est vraiment pas difficile du tout lorsque vous ne faites que passer à SqlDataReader sous-jacent. C'est juste un peu de temps mais pas si mal.

Mais je suis curieux, est la ressource que vous souhaitez disposer de la connexion? Si c'est le cas, il existe un membre CloseConnection de l'énumération CommandBehavior qui garantit que la connexion sera fermée lorsque le lecteur de données est fermé.

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

Notez que Fermer/Disposer est la même chose sur SqlDataReader.

Enfin, voici une dernière suggestion qui m'a bien servi dans le passé. Notez que dans l'exemple libre suivant, vous possédez le SqlDataReader du début à la fin, même si vous "cédez" à l'appelant à chaque enregistrement.

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 
+0

La ressource est un ticket jetable qui est utilisé pour protéger l'accès à la base de données SQL. –

+0

D'accord, j'ai ajouté une autre suggestion dans ma réponse qui pourrait aider. – Josh

+0

C'est une solution très intéressante. Quelles sont les pensées des gens à ce sujet? –

3

Inverser le; utilisez votre ressource "Caché" comme élément principal, implémentez IDisposable, puis fermez DataReader lorsque vous avez terminé.

+0

Je préfèrerais vraiment que le code d'appel n'en sache rien. Il attend actuellement une instance de SqlDataReader –

+4

Possédez-vous le code appelant? Est-ce qu'il faut vraiment accepter un 'SqlDataReader'? C'est quelque chose que vous devriez essayer de ne pas faire circuler; si vous pouvez lui faire accepter un 'IDataReader' à la place, vous pouvez envelopper facilement' SqlDataReader'. – Aaronaught

+0

Oui, je possède le code d'appel. Je préférerais ne pas avoir à envelopper la classe (implémenter IDataReader) –

1

http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldatareader.aspx La classe n'est pas scellée. Vous devriez juste pouvoir appeler base.dispose() au début de votre override et ensuite mettre votre code après.

Je n'ai pas mon IDE devant moi, mais il faut regarder somthing comme

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 --- viens de lire les commentaires orginal, je vois qu'il a le constructeur privé, laissez-moi éclater mon VS2008 et mal BRB

recherche pour elle, et tout le monde essaie ces solutions de fantaisie, la seule chose que je vois qui peut être fait est

public class myClass : IDisposable 
{ 

    public SqlDataReader dataReader { get; set; } 

    #region IDisposable Members 

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

    #endregion 
} 

EDIT --- Soupir, c'est exactement ce que Silky posté il ya 40 min.

+0

Ce code provoque les ERREURS de COMPILATION: Erreur # 1 Le type «System.Data.SqlClient.SqlDataReader» n'a aucun constructeur défini ... et ... Erreur # 2 'System.Data.SqlClient.SqlDataReader.SqlDataReader (System.Data.SqlClient.SqlCommand, System.Data.CommandBehavior)' est inaccessible en raison de son niveau de protection –

+0

première ligne de votre lien: "Cette classe ne peut pas être héritée." – kristian

+0

Peut également renvoyer un Tuple de

Questions connexes