[英]Dependency Injection for WCF Custom Behaviors
在我的WCF服务中,我有一个自定义消息检查器,用于将传入消息验证为XML Schema的原始XML。 消息检查器具有一些依赖性(例如记录器和XML架构集合)。 我的问题是,我可以使用依赖注入框架(我目前正在使用Ninject)来实例化这些自定义行为并自动注入依赖项吗?
我做了一个简单的例子来展示这个概念:
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using Ninject.Extensions.Logging;
public class LogMessageInspector : IDispatchMessageInspector
{
private readonly ILogger log;
public LogMessageInspector(ILogger log)
{
this.log = log;
}
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
LogMessage(ref request);
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
LogMessage(ref reply);
}
private void LogMessage(ref Message message)
{
//... copy the message and log using this.log ...
}
}
public class LogMessageBehavior : IEndpointBehavior
{
private readonly IDispatchMessageInspector inspector;
public LogMessageBehavior(IDispatchMessageInspector inspector)
{
this.inspector = inspector;
}
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { }
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { }
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this.inspector);
}
public void Validate(ServiceEndpoint endpoint) { }
}
我如何将注册ILogger
注入LogMessageInspector
并将LogMessageInspector
注入LogMessageBehavior
?
第二个问题,这是否过度杀伤?
编辑 :如果我在代码中构建我的服务,我可以使用它,因为我使用Ninject创建行为。 但是,在通过config配置服务时,我需要添加一个扩展BehaviorExtensionElement
的附加类。 这个类是由WCF创建的,我似乎无法找到一种方法来导致由Ninject创建它。 在代码中配置:
static void Main(string[] args)
{
using (IKernel kernel = new StandardKernel())
{
kernel.Bind<IEchoService>().To<EchoService>();
kernel.Bind<LogMessageInspector>().ToSelf();
kernel.Bind<LogMessageBehavior>().ToSelf();
NinjectServiceHost<EchoService> host = kernel.Get<NinjectServiceHost<EchoService>>();
ServiceEndpoint endpoint = host.AddServiceEndpoint(
typeof(IEchoService),
new NetNamedPipeBinding(),
"net.pipe://localhost/EchoService"
);
endpoint.Behaviors.Add(kernel.Get<LogMessageBehavior>());
host.Open();
Console.WriteLine("Server started, press enter to exit");
Console.ReadLine();
}
}
这工作正常,但我不知道如何通过我的app.config
配置时创建行为:
<system.serviceModel>
<services>
<service name="Service.EchoService">
<endpoint address="net.pipe://localhost/EchoService"
binding="netNamedPipeBinding"
contract="Contracts.IEchoService"
behaviorConfiguration="LogBehaviour"
/>
</service>
</services>
<extensions>
<behaviorExtensions>
<add name="logMessages" type="Service.LogMessagesExtensionElement, Service" />
</behaviorExtensions>
</extensions>
<behaviors>
<endpointBehaviors>
<behavior name="LogBehaviour">
<logMessages />
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
public class LogMessagesExtensionElement : BehaviorExtensionElement
{
public override Type BehaviorType
{
get { return typeof(LogMessageBehavior); }
}
protected override object CreateBehavior()
{
//how do I create an instance using the IoC container here?
}
}
我如何将注册ILogger注入LogMessageInspector并将LogMessageInspector注入LogMessageBehavior?
UPDATE
如果我错了,请纠正我,但我想这个问题归结为如何在BehaviorExtensionElement.CreateBehavior
获得Ninject内核的实例? 答案取决于您的托管方案。 如果在IIS下托管,您可以向NinjectWebCommon
添加类似这样的NinjectWebCommon
:
public static StandardKernel Kernel
{
get { return (StandardKernel)bootstrapper.Kernel; }
}
由于您似乎是自托管,您可能也想要寻找内核的静态实例。 然而,在我看来,这不是一个非常好的主意。
我实际上会投票选择您自己的方法并以编程方式配置行为,除非有必要使用BehaviorExtensionElement
,因为您需要能够通过配置文件配置行为。
这有点过分吗?
这取决于,但绝对不是,如果您要对实施进行单元测试。
为什么不采用更面向对象的方法,而不是针对XML模式验证原始XML? 例如,您可以将每个操作建模为单个消息( DTO )并隐藏通用接口背后的实际逻辑。 因此,不是拥有包含MoveCustomer(int customerId, Address address)
方法的WCF服务,而是存在MoveCustomerCommand { CustomerId, Address }
类,实际逻辑将由实现ICommandHandler<MoveCustomerCommand>
接口的类实现。单个Handle(TCommand)
方法。
这种设计具有以下优点:
ICommandHandler<T>
接口( OCP )实现装饰器来添加横切关注点 当您应用基于单个通用ICommandHandler<TCommand>
接口的设计时,很容易创建可应用于所有实现的通用装饰器。 在WCF服务内部运行时可能只需要应用一些装饰器,其他应用程序类型也可能需要其他装饰器(如验证)。
消息可以定义如下:
public class MoveCustomerCommand
{
[Range(1, Int32.MaxValue)]
public int CustomerId { get; set; }
[Required]
[ObjectValidator]
public Address NewAddress { get; set; }
}
此消息定义了一个操作,该操作将CustomerId
的客户移动到提供的NewAddress
。 属性定义了哪个状态有效。 有了这个,我们可以使用.NET DataAnnotations或Enterprise Library Validation Application Block进行基于对象的验证。 这比编写基于XSD的XML验证要好得多,而这些验证是非常难以维护的。 这比你当前试图解决的复杂WCF配置要好得多。 而不是在WCF服务中烘焙此验证,我们可以简单地定义一个装饰器类,确保每个命令的验证如下:
public class ValidationCommandHandlerDecorator<TCommand>
: ICommandHandler<TCommand>
{
private ICommandHandler<TCommand> decoratedHandler;
public ValidationCommandHandlerDecorator(
ICommandHandler<TCommand> decoratedHandler)
{
this.decoratedHandler = decoratedHandler;
}
public void Handle(TCommand command)
{
// Throws a ValidationException if invalid.
Validator.Validate(command);
this.decoratedHandler.Handle(command);
}
}
这个ValidationCommandHandlerDecorator<T>
装饰器可以被任何类型的应用程序使用; 不仅是WCF。 由于WCF默认不处理任何抛出的ValidationException
,因此您可以为WCF定义一个特殊的装饰器:
public class WcfFaultsCommandHandlerDecorator<TCommand>
: ICommandHandler<TCommand>
{
private ICommandHandler<TCommand> decoratedHandler;
public WcfFaultsCommandHandlerDecorator(
ICommandHandler<TCommand> decoratedHandler)
{
this.decoratedHandler = decoratedHandler;
}
public void Handle(TCommand command)
{
try
{
this.decoratedHandler.Handle(command);
}
catch (ValidationException ex)
{
// Allows WCF to communicate the validation
// exception back to the client.
throw new FaultException<ValidationResults>(
ex.ValidationResults);
}
}
}
如果不使用DI容器,可以按如下方式创建新的命令处理程序:
ICommandHandler<MoveCustomerCommand> handler =
new WcfFaultsCommandHandlerDecorator<MoveCustomerCommand>(
new ValidationCommandHandlerDecorator<MoveCustomerCommand>(
// the real thing
new MoveCustomerCommandHandler()
)
);
handler.Handle(command);
如果您想了解有关此类设计的更多信息,请阅读以下文章:
尝试让LogMessageBehavior也使用BehaviorExtensionElement作为其基类,然后您应该能够执行以下操作:
public override Type BehaviorType
{
get { return this.GetType(); }
}
protected override object CreateBehavior()
{
return this;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.