Enregistrement des exceptions pour les Services WCF utilisant ELMAH

nous utilisons l'excellent ELMAH pour traiter les exceptions non freinées dans un ASP.NET 3.5 application web. Cela fonctionne extrêmement bien pour l'ensemble du site à l'exception des services de la WCF qui sont consommés en utilisant les fonctionnalités REST. Lorsqu'une exception se produit dans les méthodes d'exploitation qui ne sont pas traitées par le code d'application, WCF le gère de diverses façons selon les contrats de service et les paramètres de configuration. Cela signifie que l'exception ne se termine pas mise à feu ASP.NET HttpApplication.Événement d'erreur ELMAH . Les deux solutions que je connais pour y faire face sont:

la première option est extrêmement simple mais n'est pas exactement DRY . La deuxième option Ne vous demande que de décorer chaque service avec l'attribut personnalisé après avoir implémenté l'attribut et le ErrorHandler. J'ai fait cela sur la base du travail de Will mais je veux vérifier que c'est le bonne approche avant d'afficher le code.

y a-t-il un meilleur moyen que j'ai raté?

la documentation MSDN pour IErrorHandler dit que la HandleError méthode est l'endroit pour faire l'enregistrement mais ELMAH accède au context HTTP.Actuel. ApplicationInstance , qui est null dans ce méthode même si HttpContext.Le courant est disponible. Faire l'appel à Elmah dans le cadre de la méthode ProvideFault est une solution de contournement car ApplicationInstance est défini, mais cela ne correspond pas à l'intention décrite dans la documentation de L'API. est-ce que je manque quelque chose ici? la documentation indique que vous ne devez pas vous fier à la méthode HandleError étant appelé sur le thread d'opération qui peut être la raison pour laquelle ApplicationInstance est nulle dans cette portée.

59
demandé sur Martin Hollingsworth 2009-05-22 04:11:38

6 réponses

la solution de mon blog (référencée dans L'OP) était basée sur une solution existante que nous utilisions/utilisons pour modifier les codes de réponse HTTP pendant un État d'erreur.

donc, pour nous c'était un changement d'une ligne pour passer L'Exception à ELMAH. S'il y a une meilleure solution, j'aimerais le savoir aussi.

pour la postérité / référence , et amélioration potentielle-voici le code de la solution actuelle.

HttpErrorHandler et ServiceErrorBehaviourAttribute Classes

using System;
using System.ServiceModel;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.Collections.ObjectModel;
using System.Net;
using System.Web;
using Elmah;
namespace YourApplication
{
    /// <summary>
    /// Your handler to actually tell ELMAH about the problem.
    /// </summary>
    public class HttpErrorHandler : IErrorHandler
    {
        public bool HandleError(Exception error)
        {
            return false;
        }

        public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
        {
            if (error != null ) // Notify ELMAH of the exception.
            {
                if (System.Web.HttpContext.Current == null)
                    return;
                Elmah.ErrorSignal.FromCurrentContext().Raise(error);
            }
        }
    }
    /// <summary>
    /// So we can decorate Services with the [ServiceErrorBehaviour(typeof(HttpErrorHandler))]
    /// ...and errors reported to ELMAH
    /// </summary>
    public class ServiceErrorBehaviourAttribute : Attribute, IServiceBehavior
    {
        Type errorHandlerType;

        public ServiceErrorBehaviourAttribute(Type errorHandlerType)
        {
            this.errorHandlerType = errorHandlerType;
        }

        public void Validate(ServiceDescription description, ServiceHostBase serviceHostBase)
        {
        }

        public void AddBindingParameters(ServiceDescription description, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection parameters)
        {
        }

        public void ApplyDispatchBehavior(ServiceDescription description, ServiceHostBase serviceHostBase)
        {
            IErrorHandler errorHandler;
            errorHandler = (IErrorHandler)Activator.CreateInstance(errorHandlerType);
            foreach (ChannelDispatcherBase channelDispatcherBase in serviceHostBase.ChannelDispatchers)
            {
                ChannelDispatcher channelDispatcher = channelDispatcherBase as ChannelDispatcher;
                channelDispatcher.ErrorHandlers.Add(errorHandler);
            }
        }
    }
}

Exemple D'Utilisation

Décorez vos Services FMC avec L'attribut ServiceErrorBehaviour:

[ServiceContract(Namespace = "http://example.com/api/v1.0/")]
[ServiceErrorBehaviour(typeof(HttpErrorHandler))]
public class MyServiceService
{
  // ...
}
86
répondu 2012-12-19 01:23:28

lors de la création D'un BehaviorExtensionElement, il est même possible d'activer le comportement en utilisant config:

public class ErrorBehaviorExtensionElement : BehaviorExtensionElement
{
    public override Type BehaviorType
    {
        get { return typeof(ServiceErrorBehaviourAttribute); }
    }

    protected override object CreateBehavior()
    {
        return new ServiceErrorBehaviourAttribute(typeof(HttpErrorHandler));
    }
}

Config:

<system.serviceModel>
    <extensions>
      <behaviorExtensions>
        <add name="elmah" type="Namespace.ErrorBehaviorExtensionElement, YourAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
      </behaviorExtensions>
    </extensions>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <elmah />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>

ainsi, il est également possible d'utiliser ELMAH en combinaison avec les services RIA!

9
répondu riezebosch 2011-06-17 13:42:38

j'ai fait ceci basé sur le travail de Will mais je veux vérifier que c'est le approche correcte avant d'afficher le code.

je pense que c'est une excellente approche (félicitations à Will pour cette annonce!). Je ne pense pas que Will ou vous avez manqué quelque chose ici. La mise en œuvre D'IErrorHandler est la méthode privilégiée pour capturer toutes les exceptions possibles côté serveur qui pourraient sinon causer le canal de communication à être défectueux (déchiré) et donc c'est un endroit naturel pour accrocher dans certains bois comme ELMAH.

Marc

2
répondu marc_s 2009-05-22 05:25:14

C'est peut-être évident pour certains, mais je viens de passer un bon moment à essayer de comprendre pourquoi mon HttpContext.Current était nul malgré l'excellente réponse de Will Hughes. Malheureusement, j'ai réalisé que c'était parce que mon service WCF est activé par un message MSMQ.

j'ai fini par réécrire la méthode ProvideFault() :

if (HttpContext.Current == null)
{
    ErrorLog.GetDefault(null).Log(new Error(error));
}
else
{
    ErrorSignal.FromCurrentContext().Raise(error);
}
2
répondu Steve Rukuts 2013-12-05 16:47:14

je n'ai pu obtenir la réponse proposée de travailler avec un Service de Données WCF. J'ai câblé l'attribut behavior, etc, mais je n'ai quand même pas enregistré d'erreurs. Au lieu de cela, j'ai fini par ajouter ce qui suit à la mise en œuvre du service:

protected override void HandleException(HandleExceptionArgs args)
{
    Elmah.ErrorSignal.FromCurrentContext().Raise(args.Exception);
    base.HandleException(args);
}
1
répondu Daniel 2013-12-05 19:40:19

Je n'ai pas essayé de le faire explicitement avec le reste, et je n'ai pas utilisé ELMAH moi-même, mais une autre option qui mérite d'être examinée pourrait être de s'accrocher dans WCF en utilisant un IDispatchMessageInspector au lieu d'un IErrorHandler.

0
répondu tomasr 2009-05-22 02:11:03