web-dev-qa-db-fra.com

Comment empêcher un service WCF d'entrer dans un état défectueux?

J'ai un service WCF qui ne doit pas entrer dans l'état défectueux. S'il y a une exception, elle doit être enregistrée et le service doit continuer sans interruption. Le service a un contrat d'opération à sens unique et lit les messages d'un MSMQ.

Mes problèmes sont doubles:

  1. Le service semble avaler une exception/faute, je ne peux donc pas le déboguer. Comment puis-je obtenir le service pour exposer l'exception afin que je puisse me connecter ou gérer?
  2. Le service entre dans un état défectueux après l'ingestion de cette exception. Comment empêcher le service d'entrer dans un état défectueux?
34
Guy

La plupart, sinon toutes les exceptions peuvent être vues dans la trace WCF ( Configuring Tracing ) et la trace est mieux visualisée avec Service Trace Viewer .

De toute évidence, ce n'est pas quelque chose que vous devriez avoir exécuté toute la journée dans un environnement de production, mais cela aide de toute façon au dépannage.

En dehors de cela, notez que oneways peut ne pas fonctionner comme un véritable "feu et oublier" selon le SessionMode que vous utilisez. Si votre service est configuré pour SessionMode.Allowed ou même SessionMode.Required, l'opération à sens unique s'exécutera comme si elle n'était pas à sens unique (cela peut être observé lors de l'utilisation à sens unique sur netTcpBinding). Pour être franc, cependant, je ne sais pas si cela change le type d'exceptions que vous pouvez obtenir, ou quand vous les obtenez. Cependant, dans tous les cas, vous devriez obtenir une exception si la demande ne peut pas être envoyée du tout. AFAIK, le oneway "se termine" lorsqu'il est correctement interrogé côté serveur. Il y a donc une place pour les exceptions (liées au framework WCF) jusque-là (la sérialisation/désérialisation vient à l'esprit).

Ensuite, ces exceptions liées au framework sont mieux visibles (même un IErrorHandler ne les obtient pas toutes en raison du fait qu'il est appelé dans le flux de demande/réponse) en utilisant la trace/traceviewer mentionnée ci-dessus.

19
Christian.K

La documentation officielle sur la façon de gérer les défauts est ici:

avec la page principale étant à Présentation du modèle de canal

Il y a un diagramme d'état de Nice montrant comment les choses se passent:

enter image description here

28

Les exceptions feront défaut au proxy. L'AFAIK ne peut pas faire grand-chose à ce sujet: ne provoque pas d'exceptions ;-p

Je suis un peu surpris que le sens unique soit toujours à l'origine d'un problème, mais pour avaler dans les genres l, il y a 3 aspects:

  1. jetez-vous fautes ? ou exceptions? cela compte (et devrait être des "défauts")
  2. comme hack, vous pouvez activer les messages d'exception de débogage - mais désactivez-le s'il vous plaît !!!
  3. utilisez-vous l'objet de service? Je viens de bloguer sur ce sujet exact ... en gros, votre "utilisation" peut avaler l'exception. 3 options:

    • n'utilisez pas "using"
    • sous-classer le proxy et remplacer Dispose ()
    • envelopper, selon le blog
11
Marc Gravell

Habituellement, le service WCF est hébergé dans un ServiceHost, si le service WCF échoue, la seule option est de tuer le service WCF et d'en démarrer un nouveau.

Le ServiceHost a un déclencheur d'événement "Faulted" qui est activé lorsque le service WCF échoue:

ServiceHost Host = new ServiceHost(new Service.MyService());
Host.Faulted += new EventHandler(Host_faulted);
Host.Open();

Il est possible d'obtenir l'exception à l'origine du défaut, mais cela nécessite un peu plus de travail:

public class ErrorHandler : IErrorHandler
{
    public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
    {

    }

    public bool HandleError(Exception error)
    {
        Console.WriteLine("exception");
        return false;
    }
}

public class ErrorServiceBehavior : IServiceBehavior
{
    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {

    }

    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
    {

    }

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        ErrorHandler handler = new ErrorHandler();
        foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
        {
            dispatcher.ErrorHandlers.Add(handler);
        }
    }
}

ServiceHost Host = new ServiceHost(new Service.MyService());
Host.Faulted += new EventHandler(Host_faulted);
Host.Description.Behaviors.Add(new ErrorServiceBehavior());
Host.Open();

Crédits http://www.haveyougotwoods.ca/2009/06/24/creating-a-global-error-handler-in-wcf

9
Rolf Kristensen

À propos de 2) ...

L'astuce consiste à utiliser "using" et à toujours appeler Abort () sur le proxy qui a levé une exception. L'article WCF Gotcha explique tout cela.

Nous utilisons une classe de service inspirée de cet article qui englobe les appels de service. Voici un exemple de code de mon projet:

ServiceHelper<CodeListServiceClient, CodeListService.CodeListService>.Use(
    proxy => seasonCodeBindingSource.DataSource = proxy.GetSeasonCodes(brandID);
);

Et c'est le code de ServiceHelper, légèrement modifié de l'article. Jusqu'à présent, cela nous a très bien servi.

using System;
using System.ServiceModel;

namespace Sportina.EnterpriseSystem.Client.Framework.Helpers
{
    public delegate void UseServiceDelegate<TServiceProxy>(TServiceProxy proxy);

    public static class ServiceHelper<TServiceClient, TServiceInterface> where TServiceClient : ClientBase<TServiceInterface>, new() where TServiceInterface : class
    {
        public static void Use(UseServiceDelegate<TServiceClient> codeBlock)
        {
            TServiceClient proxy = null;
            bool success = false;
            try
            {
                proxy = new TServiceClient();               
                codeBlock(proxy);
                proxy.Close();
                success = true;
            }
            catch (Exception ex)
            {
                Common.Logger.Log.Fatal("Service error: " + ex);                                
                throw;
            }
            finally
            {
                if (!success && proxy != null)
                    proxy.Abort();
            }
        }
    }
}
7
David Vidmar

J'ai eu un problème où le canal est resté dans un état défectueux après une exception ReceiveTimeout. Cela rendrait le service inutilisable par les connexions ultérieures.

Le correctif pour récupérer le service de l'état défaillant était pour moi de gérer l'événement défaillant du canal de communication:

 channelFactory = new ChannelFactory<IService>(endpoint);
 channelFactory.Faulted += OnChannelFaulted;
 var channel = channelFactory.CreateChannel();

Définissez ensuite OnChannelFaulted:

 void OnChannelFaulted(object sender, EventArgs e)
 {
     channelFactory.Abort();
 }

Remarque: J'exécute la configuration WCF via du code par rapport à l'utilisation de liaisons dans Web.config.

7
rjchicago