简体   繁体   中英

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.

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>

This is interesting question but setting up DI container is not that straight forward in WCF. You have perform following setup:

  1. Create a custom Instance provider
  2. Create a custom service host
  3. If its not self-hosting then a custom ServiceHostFactory setup.

See the complete code sample on MSDN for how to setup DI in WCF . Once DI is setup you just need to change the ErrorHandler implementation to use ILogger via constructor injection:

public class MyErrorHandler : IErrorHandler
{
  private ILogger logger;
  public MyErrorHandler(ILogger logger)
   {
     this.logger = logger;
   }
}

Here's an additional source for more options of setting up InstanceProvider with another type of DI.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM