c# - 如何将对象注入(inject) WCF 的 IErrorHandler?

标签 c# .net wcf exception ierrorhandler

我有这种情况:我有 WCF 服务。我通过 MyErrorHandler 和已实现的接口(interface) IErrorHandler 处理所有异常。下面是整个工作代码。

我想做什么,但不知道怎么做:我想将一个对象(例如 ILogger)注入(inject) MyErrorHandler 类。这基本上意味着我必须在此处注入(inject)一个对象:[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 中注入(inject)一些东西 我不想对记录器使用 private static readonly 方法。

最佳答案

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

这是主机应用程序的工作代码,它已将 ILogger 注入(inject)到 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>

关于c# - 如何将对象注入(inject) WCF 的 IErrorHandler?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34299588/

相关文章:

C# - 跳过最后 X 行的 Linq 语句

c# - 更改端点地址后如何修复 'Unrecognized Message Version'

c# - 在 WCF 中使用共享数据类型作为 DataContract

c# - 如何获取 DataGridViewRowSelectedRowCollection 中的列

C#: 无法从程序集中加载类型

c# - .NET 核心与 MySQL

c++ - gsoap wsdl2h 在 wcf wsdl 文件上失败

c# - 如何为另一个对象注销另一个对象的事件处理程序?

c# - 禁用 VS2015 C# 编译器自动生成 [Serializable] 属性

c# - WCF 如何避免定义许多操作契约(Contract)