如何在 WCF 中使用 DataContract/DataMember 序列化 Func<int, int>(甚至是一般委托)类型字段
Posted
技术标签:
【中文标题】如何在 WCF 中使用 DataContract/DataMember 序列化 Func<int, int>(甚至是一般委托)类型字段【英文标题】:How to serialize a Func<int, int> (or even a general delegate) type field with DataContract/DataMember in WCF 【发布时间】:2015-07-25 21:24:20 【问题描述】:我正在尝试序列化标记为[DataMember]
的类的Func<int, int>
字段,该字段标记为[DataContract]
,其目标或 Lambda 表达式定义也是合同的一部分,甚至可能在类本身中。
我正在使用自定义DataContractResolver
,当我取消标记Func
字段时效果很好,但是当我标记它时,它不能解析序列化所需的System.DelegateSerializationHolder
类型。我尝试将此类型添加到我的自定义 DataContractResolver
,但在 System 命名空间中找不到它。
我可以在使用此类的每个客户端类的 [OnDeserialized]
方法中重新定义它,但是每次使用此类时我都需要这样做,当然我想避免这种情况(客户端类也是一部分DataContract
)。
此序列化暂时用于将应用程序的状态保存到磁盘,因此保存委托(甚至是我不感兴趣的事件)具有逻辑意义,因为它是对象图的一部分。
那么如何使用 WCF 的 DataContract
序列化这个 Func<int, int>
?
【问题讨论】:
如果你序列化一个委托,你是否也想序列化委托附加到的实例? 正如我所说,所有涉及的类都是 DataContract 的一部分,并且是序列化的一部分。 是的......但“参与”并不意味着“序列化”,“类”与“实例”不同。这就是我要求澄清的原因。 Func 字段属于一个正在序列化的类。它的定义(通常是一个 Lambda 表达式)也在一个属于被序列化的总对象图的类中。使用所有这些的类也是同一个对象图的一部分。所以它基本上是一个封闭的系统,可以序列化维护所有对象引用。 二进制序列化是否适合您? 【参考方案1】:问题在于无法为此使用默认的 WCF 序列化程序 - 委托序列化仅适用于 .NET。
解决方案是改用NetDataContractSerializer
。在您的情况下,使用持久性上下文(或 File
上下文):
var context = new StreamingContext(StreamingContextStates.Persistence);
var s = new System.Runtime.Serialization.NetDataContractSerializer();
var sb = new StringBuilder();
using (var writer = new XmlTextWriter(new StringWriter(sb)))
s.WriteObject(writer, new Test() SomeFunc = (int i) => "Hi".Dump().Length );
sb.ToString().Dump();
[DataContract]
class Test
[DataMember]
public Func<int, int> SomeFunc;
请注意,序列化程序只会维护有关委托的信息 - 如果您只为您的委托使用编译时方法,那很好,但它不会 > 为动态编译的方法工作。请注意。
避免匿名函数也是一个好主意 - 虽然它们会工作,但它们也可能在不同的构建之间发生变化,导致您的持久状态变得无效,可能带来灾难性的副作用。不要忘记,委托是通过方法+类名(以及返回类型和参数...)进行序列化的——当名称更改时,持久化的委托将指向具有旧名称的新方法。即使使用非匿名方法,也要尽量确保您永远不会更改可能作为委托持久存在的方法 - 理想情况下,如有必要,使用原始方法签名提供向后兼容的行为。
如果您发现自己添加了对网络序列化的支持,只需添加更多 StreamingContextStates
。
【讨论】:
“动态编译的方法”是什么意思。在我的情况下,直到运行时我才知道实际的Func<int, int>
,当它根据一些运行时参数由方法获取时。
@GDS 没关系。问题是如果该方法根本不存在于编译时代码中 - 例如,如果它是在运行时编译表达式树(例如LambdaExpression
)的结果。如果确实涉及运行时生成的代码,则必须添加一两个额外的层来确定性地处理它。【参考方案2】:
我知道这篇文章已有几年历史了。我找到了解决这个问题的方法,更多的是我使用了 mikecodes.net 知识。因此,请查看他的网站以获取更多信息。这是我的简化:
using Microsoft.CodeAnalysis.Scripting;
using Microsoft.CodeAnalysis.CSharp.Scripting;
using System;
public static class Script
public static Func<int, int> Compile(string code) => CSharpScript.EvaluateAsync<Func<int, int>>(code, ScriptOptions.Default).Result;
[Serializable] // DataContract stuff
public class Data
public string Code get; set;
public string Name get; set;
public int Value get; set;
private Func<int, int> _func = null;
public Func<int,int> Function
get
if(_func ==null)
_func = Script.Compile(this.Code);
return _func;
public class Program
public void Main()
var data = new Data
Code = "i=>i + 1",
Name = "Sample",
Value = 2
;
Console.WriteLine($"Function = data.Function(data.Value)");
【讨论】:
以上是关于如何在 WCF 中使用 DataContract/DataMember 序列化 Func<int, int>(甚至是一般委托)类型字段的主要内容,如果未能解决你的问题,请参考以下文章
是否可以在 WCF 服务的 DataContract 中定义位的使用?
如何拥有一个带有 json 动态成员的 WCF DataContract