2014-11-05 9 views
8

Sto utilizzando l'interfaccia IErrorHandler WCF per intercettare e registrare gli errori sul lato server di un servizio WCF. Tuttavia, la StackTrace dell'eccezione passato a HandleError e ProvideFault è incasinato:Come ottenere lo stack Eccezione accurato nell'operazione basata su attività WCF

a System.ServiceModel.Dispatcher.TaskMethodInvoker.InvokeEnd (Object esempio, Object [] & uscite, risultato IAsyncResult) al sistema .ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeEnd (MessageRpc & RPC) a System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage7 (MessageRpc & RPC) .... molto altro ancora

Non sono sorpreso di vedere i metodi di Dispatcher casuali nello stack trace, ma ho pensato che avrei visto il mio metodo in cima allo stack.

ho deciso che questo avviene solo sulle operazioni che sembrano

[OperationContract] 
public Task<int> MyOperation() 
{ 
    throw new ApplicationException("test"); 
} 

servizi che assomigliano a questo avere una corretta analisi dello stack per me di fare il login:

[OperationContract] 
public int MySyncOperation() 
{ 
    throw new ApplicationException("test"); 
} 

Come una cronaca, ecco come sono i metodi del gestore degli errori:

public class MyErrorHandler : IErrorHandler 
{ 
    public bool HandleError(Exception error) 
    { 
    //variable 'error' has wrong stack trace if exception sourced from Task<int> operation 
    return false; 
    } 
    public void ProvideFault(Exception error, MessageVersion version, ref Message fault) 
    { 
    //variable 'error' has wrong stack trace if exception sourced from Task<int> operation 
    } 
} 

Si noti che il tipo di eccezione e il messaggio ar E corretto, quindi è come se stessero erroneamente rilanciando la mia eccezione da qualche parte con un "lancio ex" piuttosto che un semplice "lancio";

C'è un modo per ottenere la traccia dello stack corretta dell'eccezione da uno dei metodi IErrorHandler?

+0

Hai mai trovato una soluzione per questo? –

+0

sfortunatamente no. Non è la fine del mondo, dal momento che il tipo di eccezione e il messaggio sono preservati, e spero che frammenti separati di registrazione che stanno accadendo nell'app mi aiutino a triangolare dove si è verificato l'errore. Non sono ancora andato in diretta, quindi non ho la chiara idea di quanto sarà efficace nella pratica – Clyde

+1

Inoltre, la mia app non avrà mai errori, quindi è un problema di odio ;-) – Clyde

risposta

13

Ho finito per risolvere questo utilizzando il seguente invocatore di operazioni personalizzate. Il mio unico obiettivo era quello di registrare gli errori con la traccia dello stack appropriata, quindi l'errore che finisce per essere gettato mantiene la traccia dello stack scadente.

public class ErrorLoggingOperationInvokerFacade : IOperationInvoker 
{ 
    private readonly IOperationInvoker _invoker; 
    private readonly ILog _log; 

    public ErrorLoggingOperationInvokerFacade(IOperationInvoker invoker, ILog log) 
    { 
     _invoker = invoker; 
     _log = log; 
    } 

    public object[] AllocateInputs() 
    { 
     return _invoker.AllocateInputs(); 
    } 

    public object Invoke(object instance, object[] inputs, out object[] outputs) 
    { 
     return _invoker.Invoke(instance, inputs, out outputs); 
    } 

    public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state) 
    { 
     return _invoker.InvokeBegin(instance, inputs, callback, state); 
    } 

    public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result) 
    { 
     var task = result as Task; 
     if (task != null && task.IsFaulted && task.Exception != null) 
     { 
      foreach (var error in task.Exception.InnerExceptions) 
      { 
       _log.Log(error); 
      } 
     } 

     return _invoker.InvokeEnd(instance, out outputs, result); 
    } 

    public bool IsSynchronous { get { return _invoker.IsSynchronous; } } 
} 

Può essere collegato con un attributo sulla classe di servizio o di un metodo:

public class LogErrorsAttribute : Attribute, IServiceBehavior, IOperationBehavior 
{ 
    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) 
    { 
     foreach (var operation in serviceHostBase.Description.Endpoints.SelectMany(endpoint => endpoint.Contract.Operations)) 
     { 
      if (!operation.Behaviors.Any(b => b is LogErrorsAttribute)) 
       operation.Behaviors.Add(this); 
     } 
    } 

    public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation) 
    { 
     dispatchOperation.Invoker = new ErrorLoggingOperationInvokerFacade(dispatchOperation.Invoker, WcfDependencyManager.ResolveLogger()); 
    } 

    public void Validate(OperationDescription operationDescription) { } 
    public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation) { } 
    public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters) { } 
    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { } 
    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { } 
} 

In precedenza ero la registrazione degli errori in un'implementazione dell'interfaccia IErrorHandler, ma a quel punto la traccia dello stack è stato già incasinato. Ho provato a modificare l'operazione invoker per lanciare un'eccezione con la traccia dello stack corretta ma non l'ho mai fatta funzionare correttamente. Per qualche ragione, le mie eccezioni di errore personalizzate si sono trasformate nelle eccezioni di errore generico, quindi ho abbandonato questo approccio.

+0

Grazie per questo. Ho aiutato il mio debug del mio problema molto rapidamente. – Ian1971

+0

Per favore, contrassegna la risposta come risposta - perché è la risposta. E grazie per aver postato la soluzione. – mark