简体   繁体   English

如何将对象注入WCF的IErrorHandler?

[英]How to inject object into WCF's IErrorHandler?

I have this situation: I have WCF service. 我有这种情况:我有WCF服务。 I'm handling all exceptions by MyErrorHandler with implemented interface IErrorHandler . 我正在通过MyErrorHandler使用实现的接口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. 我想做什么,但不知道如何做:我想将一个对象(例如ILogger)注入MyErrorHandler类。 It basically means I have to inject an object here: [GlobalErrorHandlerBehaviour(typeof(MyErrorHandler))] . 这基本上意味着我必须在这里注入一个对象: [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. 我想使用DI并在IErrorHandler注入一些内容, IErrorHandler我不想在IErrorHandler中使用private static readonly方法。

This question is related to yours. 这个问题与您有关。 Basically, you don't need GlobalErrorHandlerBehaviourAttribute . 基本上,您不需要GlobalErrorHandlerBehaviourAttribute You can add behaviour to your service manually. 您可以手动将行为添加到服务中。 What you have to do is to create your ServiceHost . 您要做的就是创建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 : 这是将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);
    }
}

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. 这是一个有趣的问题,但是在WCF中设置DI容器并不是那么简单。 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. 如果它不是自托管的,则自定义ServiceHostFactory安装程序。

See the complete code sample on MSDN for how to setup DI in WCF . 有关如何在WCF中设置DI的信息,请参见MSDN上的完整代码示例。 Once DI is setup you just need to change the ErrorHandler implementation to use ILogger via constructor injection: 设置DI后,您只需更改ErrorHandler实现即可通过构造函数注入使用ILogger:

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. 这里有一个额外的来源与另一种类型的DI的建立InstanceProvider更多的选择。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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