Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to serialize a Func<int, int> (or even a general delegate) type field with DataContract/DataMember in WCF

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?

like image 534
GDS Avatar asked May 14 '15 09:05

GDS


People also ask

How do you serialize an object in WCF?

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.

What is by serialization with respect to WCF?

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.

What is the type of serializer used in WCF?

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.

What is Datacontract in C#?

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.


2 Answers

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)}");
    }
}
like image 187
Shayne Pierce Avatar answered Oct 01 '22 14:10

Shayne Pierce


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.

like image 30
Luaan Avatar answered Oct 01 '22 12:10

Luaan