如何将对象注入 WCF 的 IErrorHandler?
How to inject object into WCF's IErrorHandler?
我遇到这种情况:我有WCF服务。我正在通过 MyErrorHandler
使用实现的接口 IErrorHandler
处理所有异常。下面是整个工作代码。
我想做什么,但不知道怎么做:我想将一个对象(例如 ILogger)注入 MyErrorHandler
class。这基本上意味着我必须在这里注入一个对象:[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
方法。
这是个有趣的问题,但在 WCF 中设置 DI 容器并不是那么简单。
您已执行以下设置:
- 创建自定义实例提供程序
- 创建自定义服务主机
- 如果它不是自托管的,则自定义 ServiceHostFactory 设置。
请参阅 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 的更多选项。
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 我更明确地解释了如何去做。
这是主机应用程序的工作代码,已将 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>
我遇到这种情况:我有WCF服务。我正在通过 MyErrorHandler
使用实现的接口 IErrorHandler
处理所有异常。下面是整个工作代码。
我想做什么,但不知道怎么做:我想将一个对象(例如 ILogger)注入 MyErrorHandler
class。这基本上意味着我必须在这里注入一个对象:[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
方法。
这是个有趣的问题,但在 WCF 中设置 DI 容器并不是那么简单。 您已执行以下设置:
- 创建自定义实例提供程序
- 创建自定义服务主机
- 如果它不是自托管的,则自定义 ServiceHostFactory 设置。
请参阅 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 的更多选项。
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 我更明确地解释了如何去做。
这是主机应用程序的工作代码,已将 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>