Instrumenting WCF,使用IServiceBehavior、IOperationBehavior和IParameterInspector扩展WCF
本文介绍了一种 WCF 中的高级技术或功能。
引言
WCF已成为IT解决方案领域的一个响亮词汇,它将Web服务理念革命性地带入了全新的境界,完美地实现并扩展了面向服务架构,并且为自由设计和构建灵活的解决方案开辟了道路。
背景
我的初衷是谈论WCF一个非常重要的特性——可扩展性。然而,我在网上发现了一系列非常优秀的文章,它们以非常详细、简单而专业的方式探讨了可扩展性,我无法与之匹敌。因此,我决定简要介绍一下可扩展性,并将读者引导至 Carlos Figueira在MSDN博客上的那篇精彩文章。
在接下来的内容中,我将向您展示如何通过三个扩展点以一种非常简单的方式扩展WCF,如果您想了解更多,可以阅读上面的链接。
使用代码
本文要求您熟悉WCF的主要概念,如服务、契约、行为、绑定等。
我将构建一个简单的WCF服务,并在三个地方扩展该服务,或者向您展示如何在三个地方扩展该服务,同时通过跟踪来展示执行路径。让我们从考虑这个简单的契约开始:
[ServiceContract(Name = "PasswordGenerator")]
public interface IPasswordGenerator
{
[OperationContract(Name = "GeneratePassword")]
string GeneratePassword();
[OperationContract(Name="ParameterizedPasswordGenerator")]
string GeneratePassword(int length);
}
以及这个实现:
public class PasswordGenerator:IPasswordGenerator
{
[OperationBehavior()]
public string GeneratePassword()
{
return "You called GeneratePassword()";
}
[OperationBehavior()]
public string GeneratePassword(int length)
{
return "You called GeneratePassword(int length) overload";
}
}
主机应用程序和客户端应用程序为了简洁和简单,被合并到这个小应用程序中,因为这超出了本文的范围。在此,我想提请您注意以下几点:
- 选择命名管道绑定,您可以选择任何您想要的;
- 主机接受的是服务类,**而不是**接口。
- 终结点接受的是接口类型。
- 在使用时,主机应该保持打开状态,完成后应将其关闭。
- 工厂使用类,**而不是**接口。
- 当不再需要时,也应该将其关闭。
- 我使用了 `GeneratePassword` 的两个重载,但由于我使用的是原始程序集而不是代理生成的,因此它们保留了方法的原始名称。
static void Main(string[] args)
{
ServiceEndpoint serviceEndpoint;
Uri basePipeAddress = new Uri(@"net.Pipe:///Password/Mine");
using (ServiceHost host =
new ServiceHost(typeof(Password.PasswordGenerator), basePipeAddress))
{
serviceEndpoint = host.AddServiceEndpoint(typeof(
Password.IPasswordGenerator), new NetNamedPipeBinding(), string.Empty);
host.Open();
using (var factory = new
ChannelFactory<password.passwordgenerator>(new NetNamedPipeBinding()))
{
var proxy = factory.CreateChannel(serviceEndpoint.Address);
proxy.GeneratePassword();
proxy.GeneratePassword(1500);
factory.Close();
}
host.Close();
}
}
上面的代码足以使用高效的命名管道绑定来托管和消费服务。到目前为止,这只是设置环境。现在我们需要创建三个扩展并记录所有内容,以便查看哪个扩展在何时执行。我不会在任何扩展中编写有用的代码,只是告诉您这是您可以完全访问WCF并操纵其所有操作的地方。这是日志记录类。
public static class Logger
{
private static void Log(string message)
{
try
{
using (var stream = new StreamWriter(@"G:\log.log", true))
{
stream.WriteLine(message);
stream.Flush();
stream.Close();
}
}
catch (Exception)
{
}
}
public static void Log(string className, string methodName, string parameter)
{
Log(string.Format("Class {0} called method {1} with parameter {2} @ {3}",
className, methodName, parameter, DateTime.Now));
}
}
第一个扩展
通过实现 `IserviceBehavior` 接口来扩展服务行为。此接口可用于检查或更改整个服务描述和运行时。
public interface IServiceBehavior
{
void Validate(ServiceDescription serviceDescription, ServiceHostBaseserviceHostBase);
void AddBindingParameters(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase, Collection<ServiceEndpoint>
endpoints,BindingParameterCollection bindingParameters);
void ApplyDispatchBehavior
(ServiceDescription serviceDescription,ServiceHostBase serviceHostBase);
}
Validate
:确保服务(或主机)不违反该行为的验证逻辑,它提供了检查服务主机和服务描述的能力,以确认服务可以成功运行。
AddBindingParameters
:用于将信息传递给绑定元素本身,您可以在此将自定义数据传递给绑定元素以支持契约实现。
与仅调用一次的其他方法不同,此方法将为服务中定义的每个终结点调用一次。
ApplyDispatchBehavior
提供了更改运行时属性值或插入自定义扩展对象的能力,例如错误处理程序、消息或参数拦截器、安全扩展和其他自定义扩展对象。
这是实现
public class MyServiceBehaviorAttribute : Attribute, IServiceBehavior
{
public void AddBindingParameters(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase,
Collection<ServiceEndpoint> endpoints,
BindingParameterCollection bindingParameters)
{
Logger.Log("MyServiceBehaviorAttribute",
"AddBindingParameters", serviceDescription.Name);
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase)
{
Logger.Log("MyServiceBehaviorAttribute",
"ApplyDispatchBehavior", serviceDescription.Name);
}
public void Validate(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase)
{
Logger.Log("MyServiceBehaviorAttribute",
"Validate", serviceDescription.Name);
}
}
第二个扩展
扩展 `IOperationBehavior`,此行为的范围仅限于一个方法,我们在此仅用于添加第三个扩展。但您可以根据需要修改它。
public interface IOperationBehavior
{
void AddBindingParameters(OperationDescription operationDescription,
BindingParameterCollection bindingParameters);
void ApplyClientBehavior(OperationDescription operationDescription,
ClientOperation clientOperation);
void ApplyDispatchBehavior(OperationDescription operationDescription,
DispatchOperation dispatchOperation);
void Validate(OperationDescription operationDescription);
}
AddBindingParameters
:在运行时将数据传递给绑定以支持自定义行为。
ApplyClientBehavior
:实现对客户端跨操作的修改或扩展。
ApplyDispatchBehavior
:实现对服务跨操作的修改或扩展。
Validate
:实现以确认操作满足某些预期标准。
public class MyOperationBehavior:Attribute, IOperationBehavior
{
public void AddBindingParameters(OperationDescription operationDescription,
System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
Logger.Log("MyOperationBehavior",
"AddBindingParameters", operationDescription.Name);
}
public void ApplyClientBehavior(OperationDescription operationDescription,
System.ServiceModel.Dispatcher.ClientOperation clientOperation)
{
clientOperation.ParameterInspectors.Add(new MyParameterInspector());
Logger.Log("MyOperationBehavior",
"ApplyClientBehavior", operationDescription.Name);
}
public void ApplyDispatchBehavior(OperationDescription operationDescription,
System.ServiceModel.Dispatcher.DispatchOperation dispatchOperation)
{
dispatchOperation.ParameterInspectors.Add(new MyParameterInspector());
Logger.Log("MyOperationBehavior",
"ApplyDispatchBehavior", operationDescription.Name);
}
public void Validate(OperationDescription operationDescription)
{
Logger.Log("MyOperationBehavior", "Validate", operationDescription.Name);
}
}
第三个扩展
扩展 `IparameterInspector` 接口,这是一种在所有处理完成后拦截请求/响应的方法(或在将它们打包到出站消息之前),以提取传入消息中的参数。
public interface IParameterInspector
{
void AfterCall(string operationName,
object[] outputs, object returnValue, object correlationState);
object BeforeCall(string operationName, object[] inputs);
}
AfterCall
:在客户端调用返回后,服务响应发送之前调用。
BeforeCall
:在客户端调用发送之前,服务响应返回之后调用。
class MyParameterInspector:IParameterInspector
{
public void AfterCall(string operationName, object[] outputs,
object returnValue, object correlationState)
{
Logger.Log("MyParameterInspector", "AfterCall", operationName);
}
public object BeforeCall(string operationName, object[] inputs)
{
Logger.Log("MyParameterInspector", "BeforeCall", operationName);
return null;
}
}
现在我们几乎完成了,最后一步是应用新的行为到服务并运行它,以便我们能够看到调用的顺序。
请注意,您可以通过逗号分隔或添加另一个属性来将多个行为应用于服务或方法,如下面的代码所示:
[MyServiceBehaviorAttribute()]
public class PasswordGenerator:IPasswordGenerator
{
[OperationBehavior(),MyOperationBehavior]
public string GeneratePassword()
{
return "You called GeneratePassword()";
}
[OperationBehavior()]
[MyOperationBehavior]
public string GeneratePassword(int length)
{
return "You called GeneratePassword(int length) overload";
}
结果
结果显示如下:
- 首先,`Service` 行为进行验证。
- 然后,`OperationBehavior` 为第一个方法进行验证。
- 然后,`OperationBehavior` 为第二个方法进行验证。
- 然后,`Servicebehavior AddBinding` 参数。
- 然后,`Operation` 行为为每个被调用的方法调用 `AddbindingParameters`。
- 然后,`Servicebehavior ApplyDispatchBehavior`。
- 然后,`Operation` 行为为每个被调用的方法调用 `ApplyDispatchBehavior`。
- 最后,它为每个被调用的方法调用 `BeforeCall` 和 `AfterCall`。
结果将类似于这样:
Class MyServiceBehaviorAttribute called method Validate
with parameter PasswordGenerator @ 5/27/2013 10:32:26 PM
Class MyOperationBehavior called method Validate
with parameter GeneratePassword @ 5/27/2013 10:32:26 PM
Class MyOperationBehavior called method Validate
with parameter ParameterizedPasswordGenerator @ 5/27/2013 10:32:26 PM
Class MyServiceBehaviorAttribute called method AddBindingParameters
with parameter PasswordGenerator @ 5/27/2013 10:32:26 PM
Class MyOperationBehavior called method AddBindingParameters
with parameter GeneratePassword @ 5/27/2013 10:32:26 PM
Class MyOperationBehavior called method AddBindingParameters
with parameter ParameterizedPasswordGenerator @ 5/27/2013 10:32:26 PM
Class MyServiceBehaviorAttribute called method ApplyDispatchBehavior
with parameter PasswordGenerator @ 5/27/2013 10:32:26 PM
Class MyOperationBehavior called method ApplyDispatchBehavior
with parameter GeneratePassword @ 5/27/2013 10:32:27 PM
Class MyOperationBehavior called method ApplyDispatchBehavior
with parameter ParameterizedPasswordGenerator @ 5/27/2013 10:32:27 PM
Class MyParameterInspector called method BeforeCall
with parameter GeneratePassword @ 5/27/2013 10:32:27 PM
Class MyParameterInspector called method AfterCall
with parameter GeneratePassword @ 5/27/2013 10:32:27 PM
Class MyParameterInspector called method BeforeCall
with parameter ParameterizedPasswordGenerator @ 5/27/2013 10:32:27 PM
Class MyParameterInspector called method AfterCall
with parameter ParameterizedPasswordGenerator @ 5/27/2013 10:32:27 PM
关注点
总而言之,可扩展性是WCF的一项出色功能,它为WCF带来了极大的灵活性,使其成为一个强大的工具,您可以预见许多问题,并控制服务的行为。