如何将对象注入WCF的IErrorHandler?

时间:2015-12-15 21:19:32

标签: c# .net wcf exception-handling ierrorhandler

我遇到这种情况:我有WCF服务。我正在使用已实现的接口MyErrorHandler处理IErrorHandler的所有异常。整个工作代码如下。

我想做什么,但不知道如何:我想将一个对象(例如ILogger)注入MyErrorHandler类。它基本上意味着我必须在这里注入一个对象:[GlobalErrorHandlerBehaviour(typeof(MyErrorHandler))]。你能帮我解决这个问题吗?

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

顺便说一下。我想使用DI并在IErrorHandler中注入一些东西我不想在记录器中使用private static readonly方法。

2 个答案:

答案 0 :(得分:6)

This问题与您的问题有关。基本上,您不需要GlobalErrorHandlerBehaviourAttribute。您可以手动向服务添加行为。您要做的就是创建ServiceHost。在this answer中,我更明确地解释了如何做到这一点。

以下是主机应用程序的工作代码,它已将ILogger注入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);
    }
}

配置:

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

答案 1 :(得分:0)

这是一个有趣的问题但是在WCF中设置DI容器并不是那么简单。 您已执行以下设置:

  1. 创建自定义实例提供程序
  2. 创建自定义服务主机
  3. 如果它不是自托管,那么自定义ServiceHostFactory设置。
  4. 请参阅MSDN上how to setup DI in WCF的完整代码示例。设置DI后,您只需要通过构造函数注入更改ErrorHandler实现以使用ILogger:

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

    这是一个additional source,有关使用其他类型的DI设置InstanceProvider的更多选项。