Search code examples
c#.netwcfexceptionierrorhandler

How to inject object into WCF's IErrorHandler?


I have this situation: I have WCF service. I'm handling all exceptions by MyErrorHandler with implemented interface IErrorHandler. There whole working code is below.

What I want to do, but have no idea how: I want to inject an object (for example ILogger) into MyErrorHandler class. It basically means I have to inject an object here: [GlobalErrorHandlerBehaviour(typeof(MyErrorHandler))]. Could you please help me solve this problem?

[ServiceContract]
public interface ITestService
{
    [OperationContract]
    int GetTest();
}

[GlobalErrorHandlerBehaviour(typeof(MyErrorHandler))]
public class TestService : ITestService
{
    public TestService(ILogger logger)
    {
        // Here, I'm already injecting logger. 
        // It's not imported for my question so I removed it for now
    }
    public int GetTest()
    {
       throw new Exception("Test");
    }
}

// This is attribute added to TestService class
// How can I inject (via constructor) ILogger, or any other class?? 
public class GlobalErrorHandlerBehaviourAttribute : Attribute, IServiceBehavior
{
    private readonly Type errorHandlerType;
    public GlobalErrorHandlerBehaviourAttribute(Type errorHandlerType)
    {
        this.errorHandlerType = errorHandlerType;
    }

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

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        IErrorHandler handler = (IErrorHandler)Activator.CreateInstance(errorHandlerType);

        foreach(ChannelDispatcherBase channelDispatcherBase in serviceHostBase.ChannelDispatchers)
        {
            ChannelDispatcher channelDispatcher = channelDispatcherBase as ChannelDispatcher;
            if (channelDispatcher != null)
            {
                channelDispatcher.ErrorHandlers.Add(handler);
            }
        }
    }

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

public class MyErrorHandler : IErrorHandler
{

    //--------------------------------------------------// 
    // I MUST INJECT ILOGGER HERE (or any other object) //
    //--------------------------------------------------//


    public bool HandleError(Exception error)
    {
        return true;
    }

    public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
    {
        FaultException fe = new FaultException();
        MessageFault message = fe.CreateMessageFault();
        fault = Message.CreateMessage(version, message, null);
    }
}

Btw. I want to use DI and inject something in IErrorHandler I don't want to use private static readonly method with logger.


Solution

  • This question is related to yours. Basically, you don’t need GlobalErrorHandlerBehaviourAttribute. You can add behaviour to your service manually. What you have to do is to create your ServiceHost. In this answer I explained more explicitly how to do it.

    Here is the working code of host application, that has injected ILogger into IErrorHandler:

    using System;
    using System.Collections.ObjectModel;
    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Description;
    using System.ServiceModel.Dispatcher;
    
    namespace ConsoleHost
    {
        class Program
        {
            static void Main(string[] args)
            {
                var logger = new DummyLogger();
                var errorHandler = new TestErrorHandler(logger);
    
                ServiceHost host = new TestServiceHost(errorHandler, typeof(TestService), new Uri("net.tcp://localhost:8002"));
                host.Open();
    
                Console.WriteLine("Press enter to exit");
                Console.ReadKey();
            }
        }
    
        [ServiceContract]
        public interface ITestService
        {
            [OperationContract]
            string Test(int input);
        }
    
        public class TestService : ITestService
        {
            public string Test(int input)
            {
                throw new Exception("Test exception!");
            }
        }
    
        public class TestErrorHandler : IErrorHandler
        {
            private ILogger Logger { get; }
    
            public TestErrorHandler(ILogger logger)
            {
                Logger = logger;
            }
    
            public bool HandleError(Exception error)
            {
                Logger.Log(error.Message);
                return true;
            }
    
            public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
            {
                FaultException fe = new FaultException();
                MessageFault message = fe.CreateMessageFault();
                fault = Message.CreateMessage(version, message, null);
            }
        }
    
        public class TestServiceHost : ServiceHost
        {
            private readonly IErrorHandler errorHandler;
    
            public TestServiceHost(IErrorHandler errorHandler, Type serviceType, params Uri[] baseAddresses)
                : base(serviceType, baseAddresses)
            {
                this.errorHandler = errorHandler;
            }
    
            protected override void OnOpening()
            {
                Description.Behaviors.Add(new ErrorHandlerBehaviour(errorHandler));
                base.OnOpening();
            }
    
            class ErrorHandlerBehaviour : IServiceBehavior, IErrorHandler
            {
                private readonly IErrorHandler errorHandler;
    
                public ErrorHandlerBehaviour(IErrorHandler errorHandler)
                {
                    this.errorHandler = errorHandler;
                }
    
                bool IErrorHandler.HandleError(Exception error)
                {
                    return errorHandler.HandleError(error);
                }
    
                void IErrorHandler.ProvideFault(Exception error, MessageVersion version, ref Message fault)
                {
                    errorHandler.ProvideFault(error, version, ref fault);
                }
    
                void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
                {
                    foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers)
                    {
                        channelDispatcher.ErrorHandlers.Add(this);
                    }
                }
    
                void IServiceBehavior.AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
                {
                }
    
                void IServiceBehavior.Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
                {
                }
            }
        }
    
        // Dummy logger
        public interface ILogger
        {
            void Log(string input);
        }
    
        public class DummyLogger : ILogger
        {
            public void Log(string input) => Console.WriteLine(input);
        }
    }
    

    Config:

    <system.serviceModel>
      <services>
        <service name="ConsoleHost.TestService">
          <endpoint address="net.tcp://localhost:8002/TestService"
                    binding="netTcpBinding"
                    contract="ConsoleHost.ITestService" />
        </service>
      </services>
    </system.serviceModel>