1
votes

I am trying to use implement IErrorHandler to signal elmah to log unhandled exceptions.

I had multiple projects in my solution. I have a Utility project where I have implemented IErrorHandler.

public abstract class BaseWebServiceErrorHandler : IErrorHandler
{
    public bool HandleError(Exception error)
    {
        return false;
    }

    public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
    {
        if (error == null) return;

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

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)
    {
        var errorHandler = (IErrorHandler)Activator.CreateInstance(errorHandlerType);
        foreach (var channelDispatcherBase in serviceHostBase.ChannelDispatchers)
        {
            var channelDispatcher = channelDispatcherBase as ChannelDispatcher;
            channelDispatcher?.ErrorHandlers.Add(errorHandler);
        }
    }
}

And in one of the WCF project I have a BaseWebService class where I am trying to use the service behavior attribute

[ServiceErrorBehaviour(typeof(WebServiceErrorHandler))]
public abstract class BaseWebService : AbstractWebService
{
    public BaseWebService()
    {
        //Code logic
    }
}

public class WebServiceErrorHandler : BaseWebServiceErrorHandler
{
}

Now with the above code when an unhandled exception occurs I am getting error that the service behavior is not matching.

but when I have the definition of IErrorHandler in my BaseWebService it self it works.

[ServiceErrorBehaviour(typeof(WebServiceErrorHandler))]
public abstract class BaseWebService : AbstractWebService
{
    public BaseWebService()
    {
        //Codelogic
    }
}

//public class WebServiceErrorHandler : BaseWebServiceErrorHandler
//{
//}

public class WebServiceErrorHandler : IErrorHandler
{
    public bool HandleError(Exception error)
    {
        return false;
    }

    public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
    {
        if (error == null) return;

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

with the above implementation it works good and gets logged in Elmah too.

Am I missing some reference in the Utility project? Appreciate your suggestions.

1
I wrote this guide: Logging to ELMAH from WCF which may help. Not posting this as an answer, since I never tested this with base classes.ThomasArdal
@ThomasArdal I have done a similar implementation but not sure why it does not work with base class as you said.user824910

1 Answers

1
votes

I just tested this using this code from this post: Logging to ELMAH from WCF.

I have two projects with the following files:

  • WcfService2
    • BaseService.cs
    • IService1.cs
    • Service1.svc.cs
  • Utils
    • HttpErrorHandler.cs

Both WcfService2 and Utils reference System.ServiceModel.dll, System.ServiceModel.Web.dll and Elmah.dll

This is the content in HttpErrorHandler:

using System;
using System.Collections.ObjectModel;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;

namespace Utils
{
    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.
            {
                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 serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints,
            BindingParameterCollection bindingParameters)
        {
        }

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

BaseService.cs:

using Utils;

namespace WcfService2
{
    [ServiceErrorBehaviour(typeof(HttpErrorHandler))]
    public class BaseService
    {
    }
}

and finally Service1.svc.cs:

namespace WcfService2
{
    public class Service1 : BaseService, IService1
    {
        public string GetData(int value)
        {
            var d = 100/value;
            return string.Format("You entered: {0}", value);
        }
    }
}