This project has moved. For the latest updates, please go here.

Serialize/Deserialize Remote.Linq.Expressions

Mar 2, 2016 at 9:59 AM
I am working on a project where lambda expressions should be passed between applications via memory-mapped files (used for instance as filter criteria for some actions). I was thinking to translate the lambda expression to a remote linq expression, serialize it for instance with JSON Newtonsoft or MsgPack, transfer it as byte-array over memory-mapped files and then deserialize it on the other side, convert it back to a lambda expression and apply it.
Would this be possible with Remote.Linq ? If yes, do you have any code samples with such (de)serialization scenarios ?
Thanks in advance.
Coordinator
Mar 2, 2016 at 10:32 AM
You certainly can do so using remote linq.

The most performant serialization would be to use binary formatter.

However, in case you want a human readable format like xml or json you can do so as well.

A robust way of doing so is to use NetDataContractSerializer that relies on data contract.

There are samples for binary, xml and json available.

Please note that the formatters of the sample applications are used to serialize and deserialize requests and response objects from and to the query service and therefor also need to properly format exception objects. This is most likely not the case in your scenario so your implementation of the formatter can be simplified.
Mar 4, 2016 at 8:43 AM
Thank you for links to the samples.
Do you eventually have a sample with an "extended" binary formatter that automatically converts a Linq expression to a Remote.Linq.Expressions.LambdaExpression (and back), by "integrating" the ToRemoteLinqExpression() and ToLinqExpression() call sinto the binary serializer/deserialzer calls ?
(The idea would be to be able to use the regular .NET BinaryFormatter, with a customized serialization/deserialization for the Expression<> objects)
Coordinator
Mar 6, 2016 at 9:28 PM
This code might serve your purpose:
    using Remote.Linq;
    using System.IO;
    using System.Runtime.Serialization.Formatters.Binary;

    public static class SerializationHelper
    {
        public static byte[] Serialize(this System.Linq.Expressions.LambdaExpression lambdaExpression)
        {
            Remote.Linq.Expressions.LambdaExpression serializableExpression = lambdaExpression.ToRemoteLinqExpression();

            using (var dataStream = new MemoryStream())
            {
                var formatter = new BinaryFormatter();
                formatter.Serialize(dataStream, serializableExpression);
                dataStream.Position = 0;
                return dataStream.ToArray();
            }
        }

        public static System.Linq.Expressions.LambdaExpression Deserialize(this byte[] data)
        {
            Remote.Linq.Expressions.LambdaExpression serializableExpression;

            using (var dataStream = new MemoryStream())
            {
                dataStream.Write(data, 0, data.Length);
                dataStream.Position = 0;
                var formatter = new BinaryFormatter();
                serializableExpression = (Remote.Linq.Expressions.LambdaExpression)formatter.Deserialize(dataStream);
            }

            return serializableExpression.ToLinqExpression();
        }

        public static TFunc Compile<TFunc>(this System.Linq.Expressions.LambdaExpression lambdaExpression)
        {
            var lambda = System.Linq.Expressions.Expression.Lambda<TFunc>(lambdaExpression.Body, lambdaExpression.Parameters);
            return lambda.Compile();
        }
    }
And a sample consumer:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Text.RegularExpressions;

    class Program
    {
        static void Main(string[] args)
        {
            string[] list = new[] { "", "1", "22", "333", "4444", };


            int min = 1, max = 3;

            Expression<Func<string, bool>> linqExpression = text => 
                text.Length > min && 
                text.Length <= max && 
                Regex.IsMatch(text.Substring(text.Length - 1, 1), @"\d");


            List<string> l1 = list.Where(linqExpression.Compile()).ToList();

            byte[] serializedExpression = linqExpression.Serialize();



            LambdaExpression deserializedExpression = serializedExpression.Deserialize();

            List<string> l2 = list.Where(deserializedExpression.Compile<Func<string, bool>>()).ToList();
        }
Mar 7, 2016 at 10:02 PM
Edited Mar 7, 2016 at 10:20 PM
Thank you for the code sample. I will take a look to see if it possible to adapt it in view to build a Remote.Linq based custom serializer (ISerializable implementation) or surrogate selector for the "Expression<Func<string, bool>>" type for instance.
( I want for instance to use the "regular" BinaryFormatter for all types but "Expression<Func<string, bool>>").
Mar 14, 2016 at 6:01 PM
Is it possible with the above approach to serialize the expression, if it is a member in a class ?
For instance something like this:
    public enum MsgType
    {
        Subscribe = 0,
        Unsubscribe = 1
    }

    public class MyMessage
    {
        public string SenderName { get; set; }
        public Expression<Func<string, bool>> MessageFilter { get; set; }
        public MsgType Type { get; set; } 
    }
Coordinator
Mar 14, 2016 at 6:31 PM
Edited Mar 14, 2016 at 8:10 PM
If you want to use BinaryFormatter and ISerializable you can do like this:
using System;
using System.IO;
using System.Linq.Expressions;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using Remote.Linq;

class Program
{
    static void Main(string[] args)
    {
        MyMessage message = new MyMessage
        {
            SenderName = "Me",
            Type = MsgType.Subscribe,
            MessageFilter = msg => msg.Contains("watch this!")
        };

        byte[] serializedMessage = message.Serialize();

        MyMessage deserializedMessage = serializedMessage.Deserialize<MyMessage>();
    }
}

[Serializable]
public enum MsgType
{
    Subscribe = 0,
    Unsubscribe = 1
}

[Serializable]
public class MyMessage : ISerializable
{
    public MyMessage()
    {
    }

    protected MyMessage(SerializationInfo info, StreamingContext context)
    {
        SenderName = info.GetString("SenderName");

        Type = (MsgType)info.GetValue("Type", typeof(MsgType));

        var serializedMessageFilter = (byte[])info.GetValue("MessageFilter", typeof(byte[]));
        MessageFilter = serializedMessageFilter.DeserializeLambda<Func<string, bool>>();
    }

    public string SenderName { get; set; }

    public Expression<Func<string, bool>> MessageFilter { get; set; }

    public MsgType Type { get; set; }
        
    void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("SenderName", SenderName);
        info.AddValue("Type", Type);
        info.AddValue("MessageFilter", MessageFilter.SerializeLambda());
    }
}

public static class SerializationHelper
{
    public static byte[] Serialize(this object obj)
    {
        using (var dataStream = new MemoryStream())
        {
            var formatter = new BinaryFormatter();
            formatter.Serialize(dataStream, obj);
            dataStream.Position = 0;
            return dataStream.ToArray();
        }
    }

    public static T Deserialize<T>(this byte[] data)
    {
        using (var dataStream = new MemoryStream())
        {
            dataStream.Write(data, 0, data.Length);
            dataStream.Position = 0;
            var formatter = new BinaryFormatter();
            return (T)formatter.Deserialize(dataStream);
        }
    }

    public static byte[] SerializeLambda(this LambdaExpression lambdaExpression)
    {
        return lambdaExpression
            .ToRemoteLinqExpression()
            .Serialize();
    }

    public static Expression<TFunc> DeserializeLambda<TFunc>(this byte[] data)
    {
        return data
            .Deserialize<Remote.Linq.Expressions.LambdaExpression>()
            .ToLinqExpression()
            .ToExpression<TFunc>();
    }

    private static Expression<TFunc> ToExpression<TFunc>(this LambdaExpression expression)
    {
        return Expression.Lambda<TFunc>(expression.Body, expression.Parameters);
    }
}