.NET 5.0中原WCF的Endpoint.Behaviors等组件位置及SOAP日志实现
你通过NuGet安装的面向.NET 5+的System.ServiceModel系列包,其实并没有移除IClientMessageInspector、IEndpointBehavior、ClientRuntime.MessageInspectors这些核心接口和属性,之前找不到对应入口,本质是混淆了WCF客户端和服务端的扩展逻辑,整套实现的适配改动量非常小,具体步骤如下:
1. 调整原有消息检查器的实现
你之前写的入站、出站消息捕获逻辑可以完全保留,只需要补充消息缓冲的适配——.NET Framework下默认会帮你做消息流的内存缓冲,.NET 5+的WCF客户端默认是流式读取的,直接读取Message内容会把流耗尽,导致下游SOAP调用失败:
public class SoapLogClientMessageInspector : IClientMessageInspector { private readonly ILogger _logger; public SoapLogClientMessageInspector(ILogger logger) { _logger = logger; } public void AfterReceiveReply(ref Message reply, object correlationState) { var bufferedReply = BufferMessage(reply); _logger.Debug("Reply received: {NewLine}{MessageContent}", Environment.NewLine, bufferedReply.ToString()); // 把缓冲后的消息实例回写,保证下游能正常读取 reply = bufferedReply; } public object BeforeSendRequest(ref Message request, IClientChannel channel) { var bufferedRequest = BufferMessage(request); _logger.Debug("Sending request: {NewLine}{MessageContent}", Environment.NewLine, bufferedRequest.ToString()); request = bufferedRequest; return null; } // 消息缓冲辅助方法,避免流读取耗尽的问题 private Message BufferMessage(Message original) { if (original == null) return null; var messageBuffer = original.CreateBuffer(int.MaxValue); return messageBuffer.CreateMessage(); } }
注意:.NET Framework下WCF默认会自动对消息流做内存缓冲,因此可以直接调用
ToString()读取内容;.NET 5+的跨平台WCF客户端默认采用流式传输,不会自动缓冲,直接读取Message会消耗底层流,导致下游SOAP调用直接报错。
2. 实现自定义端点行为
很多人适配时找不到MessageInspectors属性,本质是找错了对象:这个集合是客户端侧ClientRuntime的专属成员,在自定义IEndpointBehavior的ApplyClientBehavior方法参数中就能拿到,跨平台WCF客户端包里完整保留了这个属性,和.NET Framework的行为完全一致:
public class SoapLogEndpointBehavior : IEndpointBehavior { private readonly IClientMessageInspector _inspector; public SoapLogEndpointBehavior(IClientMessageInspector inspector) { _inspector = inspector; } public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { // 无额外绑定参数需求,留空即可 } public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { // 直接把消息检查器加入客户端运行时的检查器集合 clientRuntime.MessageInspectors.Add(_inspector); } public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { // 客户端场景不需要实现服务端分发逻辑,留空即可 } public void Validate(ServiceEndpoint endpoint) { // 可按需添加端点合法性校验逻辑 } }
3. 挂载行为到客户端端点
这套注册逻辑和.NET Framework的使用方式几乎没有区别:拿到生成的WCF代理客户端实例后,直接把自定义的端点行为加入Endpoint的行为集合即可:
// 初始化WCF客户端代理 var soapClient = new YourServiceClient(binding, endpointAddress); // 初始化日志检查器,注入项目内的ILogger实例 var logInspector = new SoapLogClientMessageInspector(_logger); // 挂载端点行为 soapClient.Endpoint.Behaviors.Add(new SoapLogEndpointBehavior(logInspector));
常见场景适配
- 如果是通过依赖注入集成WCF客户端,不需要手动实例化客户端,可以在添加客户端的链式配置中直接挂载行为:
services.AddServiceClient<IYourService>(config => { // 配置绑定、服务地址的原有逻辑 }).AddEndpointBehavior<SoapLogEndpointBehavior>(); - 若运行时抛出"消息主体已被读取,无法重复读取"的异常,就是没有执行消息缓冲导致的,必须通过
CreateBuffer把消息内容加载到内存,再读取内容做日志,最后把缓冲生成的Message实例回写到ref参数中。
内容的提问来源于stack exchange,提问作者AFract

