2

First, a quick overview on what I'm trying to do: I want to take a C# expression, serialize it, send it to another process, de-serialize it, and use it to filter a list. Here's the caveat - when the expression is created it is done so going against a generic parameter type T but when it is de-serialized it needs to go against a dynamic instead. The reason for this is that when it is eventually used on a server in a different process it will be done so against a list of dynamics as I will not have type information in that context.

I feel like I'm close as I've used the Newtonsoft.Json and Serialize.Linq libraries to put together a proof of concept but I can only get it to work without using dynamics (e.g. I have the type T available on both the serializing side (client) and the de-serializing side (server). On to some code to show you what I have...

Given this:

public class Person
{
    public string Name { get; set; }
    public string Email { get; set; }
}

...as the type we are working with. I have a client that has interface:

public interface IClient
{
    T Get<T>(Expression<Func<T, bool>> query);
}

...so that I can do this:

var client = new Client();
var john = client.Get<Person>(p => p.Name == "John");

...all is well and good so far. In the client.Get() method I am taking the passed expression and serializing it down and sending to the server. The server looks as such:

 public dynamic Server(string serializedExpression)
 {
    var people = new List<dynamic>
    {
        new { Name = "John", Email = "john@stackoverflow.com" },
        new { Name = "Jane", Email = "jane@stackoverflow.com" }
    };

    var serializer = new ExpressionSerializer(new JsonSerializer());
    var deserializedExpression = (Expression<Func<dynamic, bool>>)serializer.DeserializeText(serializedExpression);

    return people.FirstOrDefault(deserializedExpression.Compile());
}

...and here is where the problems happen because I'm trying to deserialize it into a type of

Expression<Func<dynamic, bool>> 

...instead of...

Expression<Func<Person, bool>>.

So my questions are:

1) Is what I'm trying to do even possible? It seems like using an ExpressionVisitor you can change the generic parameter types and I've tried to do so to change from Person to dynamic before I serialize and send but have had no luck.

2) Is there a better way to do what I want to accomplish? I know the first question will be why don't I just get access to the type T specified in the expression Func<> on the server but that won't be possible due to the nature of the server. I basically want the luxury of using Linq on the client to specify query predicates while executing those predicates against dynamic lists.

Thanks in advance for any answers or ideas you can provide.

Regards,

Craig

Craig Koster
  • 496
  • 5
  • 15
  • Possibly dumb suggestion, but would it be possible to be "not quite dynamic" on the server by having some base `IMyObject` interface or class that you know everything inherits from? – Nate Barbettini Dec 12 '14 at 00:01
  • Question after trying to figure this out. Is the list you are actually trying to filter going to be a collection of `dynamics` or will it be a static type? – TyCobb Dec 12 '14 at 00:27
  • Nate - not a dumb suggestion and I've thought to go that way but as soon as I mark my expressions with IMyObject then I lose the ability (AFAIK) to do meaningful lambda's in my expression as I'll be constrained to only doing operations on IMyObject members unless I do a bunch of casting which I want to avoid. – Craig Koster Dec 12 '14 at 00:36
  • I should specify I am actually talking about having a collection that is of the actual `Person` and not an anonymous type (`new {...}`). Not so much the `T` on the list. – TyCobb Dec 12 '14 at 00:36
  • Ty - the list on the server that I want to actually apply the expression to will be a collection of dynamics as I won't have any relevant type information in that process context. The server has no knowledge of what a Person type is as its data is all loaded from JSON into dynamics. The thing is the data on the server will *match* the defined type Person so the expression should operate correctly against these dynamics as they'll have all the same properties that were defined by the Person type on the client. Does that make sense? – Craig Koster Dec 12 '14 at 00:39
  • Well, you might not necessarily lose the ability to do meaningful lambdas if `Person` inherits from `IMyObject`, but we'd probably be back in the same boat of the server not knowing what to do with the expression. :( – Nate Barbettini Dec 12 '14 at 00:41
  • Yes, that clears things up and throws my idea out the window. =P Good luck! Looks like @NateBarbettini has a good possible solution. – TyCobb Dec 12 '14 at 00:41
  • If the data on the server will match the defined type as you said, then the extension methods I used in my "bad" solution will be useful so we can move away from `List`. But as far as deserializing the expression without knowledge of the type, that's tricky (clearly). – Nate Barbettini Dec 12 '14 at 00:43

1 Answers1

2

LINQ doesn't like dynamics in Expressions much. (DLINQ perhaps?)

Alternatively, you could pass along a hint to the server about which object type you're using. I realize this is probably not what you're looking for, but it is working:

(borrowed from this CodeProject article)

public static class Extensions
{
    public static object ToType<T>(this object obj, T type)
    {

        //create instance of T type object:
        var tmp = Activator.CreateInstance(Type.GetType(type.ToString()));

        //loop through the properties of the object you want to covert:          
        foreach (PropertyInfo pi in obj.GetType().GetProperties())
        {
            try
            {

                //get the value of property and try 
                //to assign it to the property of T type object:
                tmp.GetType().GetProperty(pi.Name).SetValue(tmp,
                                          pi.GetValue(obj, null), null);
            }
            catch { }
        }

        //return the T type object:         
        return tmp;
    }

    public static object ToNonAnonymousList<T>(this List<T> list, Type t)
    {

        //define system Type representing List of objects of T type:
        var genericType = typeof(List<>).MakeGenericType(t);

        //create an object instance of defined type:
        var l = Activator.CreateInstance(genericType);

        //get method Add from from the list:
        MethodInfo addMethod = l.GetType().GetMethod("Add");

        //loop through the calling list:
        foreach (T item in list)
        {

            //convert each object of the list into T object 
            //by calling extension ToType<T>()
            //Add this object to newly created list:
            addMethod.Invoke(l, new object[] { item.ToType(t) });
        }

        //return List of T objects:
        return l;
    }

}

... with some not-so-nice branching on the server:

public interface IClient
{
    T Get<T>(Expression<Func<T, bool>> query);
}

public class Person
{
    public string Name { get; set; }
    public string Email { get; set; }
}

public class Client
{
    public T Get<T>(Expression<Func<dynamic, bool>> query)
    {
        var serializer = new ExpressionSerializer(new JsonSerializer());
        var serializedExpression = serializer.SerializeText(query);
        return (T)Server.Retrieve(serializedExpression, typeof(T).FullName);
    }
}

public static class Server
{
    public static dynamic Retrieve(string serializedExpression, string targetType)
    {
        var people = new List<dynamic> 
            {
                new { Name = "John", Email = "john@stackoverflow.com" },
                new { Name = "Jane", Email = "jane@stackoverflow.com" }
            };

        // Try creating an object of the type hint passed to the server
        var typeInstance = Activator.CreateInstance(Type.GetType(targetType));

        if (typeInstance.GetType() == typeof(Person))
        {
            var serializer = new ExpressionSerializer(new JsonSerializer());
            var deserializedExpression = (Expression<Func<Person, bool>>)serializer.DeserializeText(serializedExpression);
            var peopleCasted = (IEnumerable<Person>)people.ToNonAnonymousList(typeof(Person));
            return peopleCasted.Where(deserializedExpression.Compile()).SingleOrDefault();
        }
        else
        {
            throw new ArgumentException("Type is unknown");
        }
    }
}

and a working test:

[TestClass]
public class UnitTest1
{
    [TestMethod]
    public void MyTestMethod()
    {
        var client = new Client();
        var john = client.Get<Person>(p => p.Name == "John");
        Assert.IsNotNull(john);
    }
}
Community
  • 1
  • 1
Nate Barbettini
  • 51,256
  • 26
  • 134
  • 147
  • Thanks for the suggestion, Nate, but unless I'm missing something I think that I may not have emphasized in the question enough that the Server() in my example is running in a completely separate process than the client - usually on another machine. That's really the crux of the problem as my server process has no knowledge of the Person type so it cannot do any kind of reflection on it as the app domain will not be able to find the type. I've tried some solutions where I serialize the type information down and try to dynamically create the type on the server but had no luck there either. – Craig Koster Dec 12 '14 at 00:46
  • Ah, ok. So there's absolutely no way of knowing anything about `Person` within `Server()` at all? (Even if you just make copies of the class definitions, or something?) – Nate Barbettini Dec 12 '14 at 01:15
  • Well I don't think so but I could be wrong - I did try to go down that route of sending the type information serialized to the server and then use that to create a type on the server via AssemblyBuilder.DefineDynamicModule() but I had no success with that, unfortunately. Also with that strategy you have to deal with child types and stuff and it seems like it'll get messy pretty fast. – Craig Koster Dec 12 '14 at 01:19
  • ...but to your question more directly - there's no way to share type information with the server at compile time. I'm not sure if it's possible to at run time via passing in serialized type info or some other way. – Craig Koster Dec 12 '14 at 01:23
  • Gotcha. Yeah, was looking at [this](http://stackoverflow.com/questions/929349/is-there-a-way-to-build-a-new-type-during-runtime) and wondering if it'd be possible to write a custom deserializer that could reconstruct the target type on the other end. I imagine object inheritance would be a huge PITA. – Nate Barbettini Dec 12 '14 at 01:26
  • Along those lines (and also an annoyingly large amount of work), you could write a custom class that could serialize the Expression query into your own format, and then parse it on the other end. The main problem here is that (AFAIK) LINQ isn't going to be able to evaluate the expression without knowing at least something about the shape of the data. – Nate Barbettini Dec 12 '14 at 01:48