Pergunta

Estamos usando o excelente ELMAH para lidar com exceções não tratadas em um 3,5 web ASP.NET inscrição. Isso funciona muito bem para todos do site para além de serviços WCF que estão sendo consumidos usando os recursos REST. Quando uma exceção ocorre dentro dos métodos de operação que não é tratada pelo código do aplicativo, WCF manipula-lo de várias maneiras, dependendo dos contratos de serviços e configurações. Isto significa que a exceção não acabam disparando o evento ASP.NET HttpApplication.Error que ELMAH usos . As duas soluções que eu estou ciente de que lidar com isso são:

  • Enrole todas as chamadas de método em um try {} catch (Exception ex) {Elmah.ErrorSignal.FromCurrentContext () Raise (ex.); lançar; } Para chamar explicitamente Elmah dentro do bloco catch.
  • IErrorHandler como descrito em < a href = "http://will.hughesfamily.net.au/about/" rel = "noreferrer"> Will Hughes' post no blog fazendo WCF e ELMAH jogo agradável juntos para fator fora a chamada para ELMAH a um ErrorHandler separado.

A primeira opção é extremamente simples, mas não é exatamente DRY . A segunda opção só exige que você decorar cada serviço com o atributo personalizado depois de implementar o atributo eo ErrorHandler. Eu tenho feito isso com base em trabalho de Will, mas eu quero verificar que este é o abordagem correta antes de postar o código.

Existe uma maneira melhor que eu perdi?

O documenation MSDN para IErrorHandler diz que o HandleError método é o lugar para fazer o registro, mas ELMAH acessos o HttpContext.Current. ApplicationInstance , que é nulo dentro deste método embora HttpContext.Current está disponível. Fazendo a chamada para Elmah dentro do método ProvideFault é uma solução alternativa como ApplicationInstance é conjunto, mas isso não corresponde à intenção descrito na documentação da API. Am I faltando alguma coisa aqui? A documentação faz afirmar que você não deve confiar no método HandleError está sendo chamado no segmento de operação que pode ser por isso que ApplicationInstance é nulo neste âmbito.

Foi útil?

Solução

A solução do meu blog (referenciado no OP) foi baseado em uma solução existente que foram / estão usando para códigos de resposta HTTP modificar-se durante um estado de erro.

Assim, para nós foi uma mudança de uma linha para passar a exceção para ELMAH. Se há uma solução melhor, eu adoraria saber sobre isso também.

Para a posteridade / referência, e melhoria potencial - aqui está o código da solução atual.

HttpErrorHandler e Classes ServiceErrorBehaviourAttribute

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);
            }
        }
    }
}

Exemplo de Utilização

Decore o seu serviços WCF com o atributo ServiceErrorBehaviour:

[ServiceContract(Namespace = "http://example.com/api/v1.0/")]
[ServiceErrorBehaviour(typeof(HttpErrorHandler))]
public class MyServiceService
{
  // ...
}

Outras dicas

Ao criar um BehaviorExtensionElement é ainda possível ativar o comportamento usando 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>

Dessa forma, também é possível usar ELMAH em combinação com serviços RIA!

Eu tenho feito isso baseado no trabalho de Will mas eu quero verificar que este é o abordagem correta antes de lançar a código.

Eu acho que esta é uma grande abordagem (parabéns a Will para esta postagem!). Eu não acho que Will ou você perdeu alguma coisa aqui. Implementando IErrorHandler é a forma preferida de capturar todas as exceções possíveis do lado do servidor que poderiam fazer com que o canal de comunicação para ser criticado (derrubadas) e, portanto, é um lugar natural para ligar em algum registro de como ELMAH.

Marc

Esta pode muito bem ser óbvio para algumas pessoas, mas eu só passei um bom tempo tentando descobrir por que o meu HttpContext.Current era nula apesar de seguir todos de excelente resposta Will Hughes. Embarassingly, percebi que isso era porque o meu serviço WCF é ativada por uma mensagem MSMQ.

Eu acabei de reescrever o método ProvideFault():

if (HttpContext.Current == null)
{
    ErrorLog.GetDefault(null).Log(new Error(error));
}
else
{
    ErrorSignal.FromCurrentContext().Raise(error);
}

eu era incapaz de obter o funcionamento resposta proposta com um serviço de dados do WCF. I com fio até o atributo comportamento, etc, mas ainda não obteve quaisquer erros registados. Em vez disso, acabei acrescentando o seguinte para a implementação do serviço:

protected override void HandleException(HandleExceptionArgs args)
{
    Elmah.ErrorSignal.FromCurrentContext().Raise(args.Exception);
    base.HandleException(args);
}

Eu não tentei fazer isso explicitamente com o material REST, e não usei ELMAH mim mesmo, mas outra opção a pena olhar em poderia ser a de ligar para WCF usando um IDispatchMessageInspector em vez de um IErrorHandler.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top