2010-04-08 16 views
3

Je n'ai trouvé aucune documentation sur la fermeture correcte des connexions de base de données dans les opérations de service WCF. J'ai un service qui renvoie une réponse en streaming par la méthode suivante. Je ne peux pas fermer la connexion au sein de la méthode ou le streaming du message de réponse sera terminé. Puisque le contrôle retourne au système WCF après l'achèvement de cette méthode, je ne sais pas comment je peux fermer cette connexion par la suite. Toute suggestion ou indication de documentation supplémentaire serait appréciée.Comment fermer une connexion de base de données utilisée pour produire un résultat de diffusion dans un service WCF?

Répondre

4

Bonne question, en fait. Malheureusement, pour lequel je crois qu'il n'y a pas de bonne réponse. Il existe en fait un ticket Microsoft Connect actif sur ce problème.

Normalement, si vous voulez diffuser les résultats et vous avez juste besoin d'un SqlDataReader régulier, vous pouvez utiliser la surcharge ExecuteReader qui prend CommandBehavior, et plus particulièrement CommandBehavior.CloseConnection. Si un lecteur est créé en utilisant ce comportement de commande, alors quand vous Close (ou Dispose) le lecteur, il ferme également la connexion sous-jacente, donc vous n'avez jamais à vous soucier de disposer du SqlConnection.

Malheureusement, il n'y a pas de surcharge équivalente de ExecuteXmlReader. Vous devez disposer du SqlConnection explicitement. Un moyen de contourner ce serait d'implémenter votre propre descendant XmlReader, en enveloppant le vrai XmlReader obtenu à partir de ExecuteXmlReader et en forçant la connexion fermée à la fermeture.

L'idée de base est simplement de dériver de XmlReader et d'envelopper le vrai XmlReader et le SqlConnection lui-même. Quelque chose comme ceci:

class SqlXmlReader : XmlReader 
{ 
    private SqlConnection connection; 
    private XmlReader reader; 

    public SqlXmlReader(SqlCommand cmd) 
    { 
     if (cmd == null) 
      throw new ArgumentNullException("cmd"); 
     this.connection = cmd.Connection; 
     this.reader = cmd.ExecuteXmlReader(); 
    } 

    public override void Close() 
    { 
     reader.Close(); 
     connection.Close(); 
    } 
} 

Cela prend la connexion et le lecteur directement à partir du SqlCommand donc il n'y a aucune chance d'une non-concordance de connexion/lecteur. Vous devez mettre en œuvre le reste des XmlReader méthodes et propriétés trop - il est juste beaucoup de méthode mandatement ennuyeux:

public override int AttributeCount 
    { 
     get { return reader.AttributeCount; } 
    } 

    public override string BaseURI 
    { 
     get { return reader.BaseURI; } 
    } 

    public override int Depth 
    { 
     get { return reader.Depth; } 
    } 

    public override bool EOF 
    { 
     get { return reader.EOF; } 
    } 

    public override string GetAttribute(int i) 
    { 
     return reader.GetAttribute(i); 
    } 

    public override string GetAttribute(string name, string namespaceURI) 
    { 
     return reader.GetAttribute(name, namespaceURI); 
    } 

    public override string GetAttribute(string name) 
    { 
     return reader.GetAttribute(name); 
    } 

    public override bool HasValue 
    { 
     get { return reader.HasValue; } 
    } 

    public override bool IsEmptyElement 
    { 
     get { return reader.IsEmptyElement; } 
    } 

    public override string LocalName 
    { 
     get { return reader.LocalName; } 
    } 

    public override string LookupNamespace(string prefix) 
    { 
     return reader.LookupNamespace(prefix); 
    } 

    public override bool MoveToAttribute(string name, string ns) 
    { 
     return reader.MoveToAttribute(name, ns); 
    } 

    public override bool MoveToAttribute(string name) 
    { 
     return reader.MoveToAttribute(name); 
    } 

    public override bool MoveToElement() 
    { 
     return reader.MoveToElement(); 
    } 

    public override bool MoveToFirstAttribute() 
    { 
     return reader.MoveToFirstAttribute(); 
    } 

    public override bool MoveToNextAttribute() 
    { 
     return reader.MoveToNextAttribute(); 
    } 

    public override XmlNameTable NameTable 
    { 
     get { return reader.NameTable; } 
    } 

    public override string NamespaceURI 
    { 
     get { return reader.NamespaceURI; } 
    } 

    public override XmlNodeType NodeType 
    { 
     get { return reader.NodeType; } 
    } 

    public override string Prefix 
    { 
     get { return reader.Prefix; } 
    } 

    public override bool Read() 
    { 
     return reader.Read(); 
    } 

    public override bool ReadAttributeValue() 
    { 
     return reader.ReadAttributeValue(); 
    } 

    public override ReadState ReadState 
    { 
     get { return reader.ReadState; } 
    } 

    public override void ResolveEntity() 
    { 
     reader.ResolveEntity(); 
    } 

    public override string Value 
    { 
     get { return reader.Value; } 
    } 

terne, terne, terne, mais cela fonctionne. Ce lecteur va fermer la connexion pour vous quand c'est fait, même comme SqlDataReader ouvert avec CommandBehavior.CloseConnection.

La dernière chose à faire serait de créer une méthode d'extension pour rendre cela plus facile à utiliser:

public static class SqlExtensions 
{ 
    public static XmlReader ExecuteSafeXmlReader(this SqlCommand cmd) 
    { 
     return new SqlXmlReader(cmd); 
    } 
} 

Une fois que vous avez cela, au lieu d'écrire:

XmlReader xr = cmd.ExecuteXmlReader(); 

Vous écrivez:

XmlReader xr = cmd.ExecuteSafeXmlReader(); 

C'est tout. Maintenant, lorsque WCF ferme votre lecteur, il ferme automatiquement la connexion sous-jacente. (Avertissement: Cela n'a pas été officiellement testé, mais je ne vois pas pourquoi cela ne fonctionnerait pas, sauf si WCF ne ferme pas le lecteur.Assurez-vous de tester cette connexion par rapport à une connexion SQL active pour vous assurer qu'elle ne fuit pas réellement les connexions.)

+0

@ Aaron: vous ne devriez pas mettre en œuvre 'IDisposable' sur cette classe? –

+0

@John: La base 'XmlReader' implémente déjà' IDisposable', et dans cette méthode invoque la méthode virtuelle 'Close', donc dans ce cas il n'est pas nécessaire de le refaire. – Aaronaught

+0

Je vois. Merci. –

1

Vous pouvez essayer une forme de service basé sur Duplex Service ou Session. Cela vous permettra de faire la demande en un seul appel et de garder le SqlConnection ouvert jusqu'à ce qu'un appel de style Disconnect() soit fait. Cette déconnexion peut .Dispose() des objets SQL associés.

1

Vous pouvez regarder dans faire votre classe de service mise en œuvre du IDispose