如何在序列化前修改WCF服务的引用类型属性?附契约示例
嘿,针对你这个WCF服务序列化前修改引用类型属性的需求,我给你整理了几个实用的方案,结合你的代码场景来具体说明:
方案一:利用IDispatchMessageInspector拦截出站消息
这个方案适合你需要全局拦截多个服务操作的返回结果,统一修改属性的场景。它能让我们在WCF框架序列化响应消息之前,拿到要返回的对象,修改后再交给框架序列化。
实现步骤:
- 实现
IDispatchMessageInspector接口,在BeforeSendReply方法里处理返回对象 - 写一个端点行为扩展,把这个检查器绑定到服务端点上
- 配置服务时添加这个行为
代码示例:
首先是消息检查器的实现:
public class PropertyModificationInspector : IDispatchMessageInspector { // 入站消息不需要处理,直接返回null public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) { return null; } public void BeforeSendReply(ref Message reply, object correlationState) { if (reply == null) return; // 尝试获取返回的ServiceProperty对象 if (reply.IsTypedMessage) { var responseObj = reply.GetBody<object>(); if (responseObj is ServiceProperty serviceProp) { // 在这里修改你的引用类型属性,比如修改某个字段或属性值 serviceProp.SomeReferenceProperty = "Modified right before serialization"; // 因为原消息的Body已经被读取过,需要重新创建消息替换 reply = Message.CreateMessage(reply.Version, reply.Headers.Action, serviceProp); } } } }
然后是端点行为扩展:
public class PropertyModificationBehavior : IEndpointBehavior { public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { } public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { } public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { // 把检查器添加到所有操作的分发运行时中 foreach (var operation in endpointDispatcher.DispatchRuntime.Operations) { operation.MessageInspectors.Add(new PropertyModificationInspector()); } } public void Validate(ServiceEndpoint endpoint) { } }
最后在服务配置中添加这个行为(代码配置示例):
var serviceHost = new ServiceHost(typeof(YourServiceImplementation)); var serviceEndpoint = serviceHost.AddServiceEndpoint( typeof(IService), new WSHttpBinding(), "http://localhost:8080/YourService"); // 添加自定义行为 serviceEndpoint.Behaviors.Add(new PropertyModificationBehavior()); serviceHost.Open();
方案二:自定义DataContractSerializer重写序列化逻辑
如果你的ServiceProperty是用DataContract标记的引用类型,这个方案适合针对特定类型做全局序列化前修改,不管哪个服务操作返回这个类型,都会自动触发修改逻辑。
实现步骤:
- 继承
DataContractSerializer,重写WriteObject方法在序列化前修改对象 - 实现自定义操作行为,替换默认的序列化器
- 将行为绑定到对应的服务操作上
代码示例:
首先是自定义序列化器:
public class CustomServicePropertySerializer : DataContractSerializer { public CustomServicePropertySerializer(Type type) : base(type) { } public override void WriteObject(XmlWriter writer, object graph) { // 序列化前检查并修改对象 if (graph is ServiceProperty serviceProp) { serviceProp.SomeReferenceProperty = "Modified by custom serializer"; } // 调用基类方法完成序列化 base.WriteObject(writer, graph); } }
然后是替换序列化器的操作行为:
public class CustomSerializerBehavior : DataContractSerializerOperationBehavior { public CustomSerializerBehavior(OperationDescription operation) : base(operation) { } public override XmlObjectSerializer CreateSerializer(Type type, string name, string ns, IList<Type> knownTypes) { // 只针对ServiceProperty类型使用自定义序列化器 if (type == typeof(ServiceProperty)) { return new CustomServicePropertySerializer(type); } return base.CreateSerializer(type, name, ns, knownTypes); } }
最后绑定到服务操作上(代码配置示例):
var serviceHost = new ServiceHost(typeof(YourServiceImplementation)); var targetEndpoint = serviceHost.Description.Endpoints.First(); // 找到GetServicePropertyAbc操作,替换其序列化器行为 var targetOperation = targetEndpoint.Contract.Operations.First(op => op.Name == "GetServicePropertyAbc"); var existingBehavior = targetOperation.Behaviors.Find<DataContractSerializerOperationBehavior>(); if (existingBehavior != null) { targetOperation.Behaviors.Remove(existingBehavior); } targetOperation.Behaviors.Add(new CustomSerializerBehavior(targetOperation)); serviceHost.Open();
方案三:在服务操作内部直接修改(最简单场景)
如果你的修改逻辑和业务强绑定,不需要全局拦截,那直接在GetServicePropertyAbc的实现方法里修改属性再返回就好了,这是最直接的方式,不需要任何WCF扩展。
代码示例:
public class YourServiceImplementation : IService { public ServiceProperty GetServicePropertyAbc() { // 先获取或构造原始的ServiceProperty对象 var serviceProp = FetchOriginalServiceProperty(); // 在返回前修改引用类型属性 serviceProp.SomeReferenceProperty = "Modified in business logic before return"; return serviceProp; } public void ModifyServicePropertyAbc() { // 你的业务实现逻辑 } public void Dispose() { // 资源释放逻辑 } // 模拟获取原始对象的方法 private ServiceProperty FetchOriginalServiceProperty() { return new ServiceProperty(); } }
方案选择建议:
- 如果你只是单个操作需要修改,优先选方案三,简单直接无额外复杂度
- 如果多个操作返回同一个类型都需要修改,选方案二,针对性强
- 如果需要全局拦截所有操作的返回结果做统一处理,选方案一
内容的提问来源于stack exchange,提问作者Jacob
相关产品推荐
相关产品推荐

