I am trying to serialize a Func<int, int>
field marked with [DataMember]
, of a class marked with [DataContract]
, whose target or Lambda expression definition is also part of the contract or could even be in the class itself.
I am using a custom DataContractResolver
which works perfectly well when I unmark the Func
field but when I mark it, it does not resolve a System.DelegateSerializationHolder
type needed for the serialization. I tried to add this type to my custom DataContractResolver
but I can't find it in the System namespace.
I could redefine it in the [OnDeserialized]
method of each client class that uses this class but then I would need to do this every time I use this class, which of course I would like to avoid (client classes are also part of the DataContract
).
This serialization is used for the time being for saving the state of the app to disk, so saving delegates (or even events which I am not interested in) makes logical sense as it is all part of the object graph.
So how can I serialize this Func<int, int>
using WCF's DataContract
?
This code constructs an instance of the DataContractSerializer that can be used only to serialize or deserialize instances of the Person class. DataContractSerializer dcs = new DataContractSerializer(typeof(Person)); // This can now be used to serialize/deserialize Person but not PurchaseOrder.
The process forms a sequence of bytes into a logical object; this is called an encoding process. At runtime when WCF receives the logical message, it transforms them back into corresponding . Net objects. This process is called serialization.
Windows Communication Foundation (WCF) uses the DataContractSerializer as its default serialization engine to convert data into XML and to convert XML back into data. The DataContractSerializer is designed to serialize data contract types.
A data contract is a formal agreement between a service and a client that abstractly describes the data to be exchanged. That is, to communicate, the client and the service do not have to share the same types, only the same data contracts.
I know this post is a few years old. I found a way to solve this, well more I used mikecodes.net knowledge. So check out his site for more info. Here is my simplification:
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)}");
}
}
The problem is that it's not possible to use the default WCF serializer for this - delegate serialization will only ever work for .NET.
The solution is to use NetDataContractSerializer
instead. In your case, with a persistence context (or File
context):
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;
}
Do note that the serializer will only maintain the information about the delegate - that's fine if you're only using compile-time methods for your delegates, but it will not work for dynamically compiled methods. Be warned.
It would also be a good idea to avoid anonymous functions - while they will work, they might also change between different builds, causing your persisted state to become invalid, possibly with disastrous side-effects. Don't forget, the delegate is serialized by method+class name (and return type, and arguments...) - when the name changes, the persisted delegate will point to the new method with the old name. Even with non-anonymous methods, try to make sure you never change the methods that might have been persisted as delegates - ideally, using the original method signature to provide backwards-compatible behaviour, if necessary.
If you ever find yourself adding support for network serialization, just add more StreamingContextStates
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With