Registrazione delle eccezioni per i servizi WCF che utilizzano ELMAH

Stiamo utilizzando l’eccellente ELMAH per gestire le eccezioni non gestite in un’applicazione Web ASP.NET 3.5. Questo funziona molto bene per tutto il sito a parte i servizi WCF che vengono consumati utilizzando le funzionalità REST. Quando si verifica un’eccezione all’interno dei metodi operativi non gestiti dal codice dell’applicazione, WCF lo gestisce in vari modi a seconda dei contratti di servizio e delle impostazioni di configurazione. Ciò significa che l’eccezione non finisce per triggersre l’ evento HttpApplication.Error di ASP.NET utilizzato da ELMAH . Le due soluzioni di cui sono a conoscenza per affrontare questo sono:

  • Includi tutte le chiamate di metodo in un try {} catch (Exception ex) {Elmah.ErrorSignal.FromCurrentContext (). Raise (ex); gettare; } per chiamare esplicitamente Elmah all’interno del blocco catch.
  • Usa IErrorHandler come descritto nel post del blog di Will Hughes. Fare in modo che WCF ed ELMAH giochino bene insieme per calcolare la chiamata a ELMAH a un ErrorHandler separato.

La prima opzione è estremamente semplice ma non è esattamente ASCIUTTA . La seconda opzione richiede solo di decorare ogni servizio con l’attributo personalizzato dopo aver implementato l’attributo e l’ErrorHandler. L’ho fatto basandomi sul lavoro di Will, ma voglio verificare che questo sia l’ approccio corretto prima di postare il codice.

C’è un modo migliore che ho perso?

La documentazione di MSDN per IErrorHandler dice che il metodo HandleError è il posto dove eseguire la registrazione ma ELMAH accede a HttpContext.Current. ApplicationInstance , che è null all’interno di questo metodo anche se HttpContext.Current è disponibile. Effettuare la chiamata a Elmah all’interno del metodo ProvideFault è una soluzione alternativa, in quanto ApplicationInstance è impostato, ma questo non corrisponde all’intento descritto nella documentazione dell’API. Mi sto perdendo qualcosa qui? La documentazione afferma che non si deve fare affidamento sul metodo HandleError che viene chiamato sul thread operativo, che potrebbe essere il motivo per cui ApplicationInstance è null in questo ambito.

La soluzione del mio post sul blog (di riferimento nell’OP) era basata su una soluzione esistente che stavamo / stiamo usando per modificare i codici di risposta HTTP durante uno stato di errore.

Quindi, per noi è stata una modifica di una riga passare l’Eccezione a ELMAH. Se c’è una soluzione migliore, mi piacerebbe saperlo anche a me.

Per Posterity / Reference e potenziale miglioramento: ecco il codice dalla soluzione corrente.

HttpErrorHandler e 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 { ///  /// Your handler to actually tell ELMAH about the problem. ///  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); } } } ///  /// So we can decorate Services with the [ServiceErrorBehaviour(typeof(HttpErrorHandler))] /// ...and errors reported to ELMAH ///  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 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); } } } } 

Esempio di utilizzo

Decora i tuoi servizi WCF con l’attributo ServiceErrorBehaviour:

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

Quando si crea un BehaviorExtensionElement è anche ansible triggersre il 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:

               

In questo modo è anche ansible utilizzare ELMAH in combinazione con i servizi RIA!

L’ho fatto basandomi sul lavoro di Will, ma voglio verificare che questo sia l’approccio corretto prima di postare il codice.

Penso che questo sia un ottimo approccio (complimenti a Will per questo post!). Non penso a Will o ti sei perso qualcosa qui. Implementare IErrorHandler è il modo migliore per catturare tutte le possibili eccezioni lato server che potrebbero altrimenti causare l’errore del canale di comunicazione (abbattuto) e quindi è un posto naturale per colbind alcune registrazioni come ELMAH.

Marc

Questo potrebbe essere ovvio per alcune persone, ma ho appena trascorso un po ‘cercando di capire perché il mio HttpContext.Current era nullo nonostante abbia seguito tutta l’eccellente risposta di Will Hughes. Imbarazzante, ho capito che questo era perché il mio servizio WCF è triggersto da un messaggio MSMQ.

Ho finito per riscrivere il metodo ProvideFault() :

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

Non ero in grado di ottenere la risposta proposta lavorando con un servizio dati WCF. Ho cablato l’attributo di comportamento, ecc., Ma non ho ancora registrato alcun errore. Invece, ho finito per aggiungere quanto segue all’implementazione del servizio:

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

Non ho provato a farlo esplicitamente con il materiale REST, e non ho usato ELMAH da solo, ma un’altra opzione che vale la pena di esaminare potrebbe essere quella di agganciare WCF usando un IDispatchMessageInspector invece di un IErrorHandler.