Serializing and Deserializing Expression Trees in C#

Serializing and Deserializing Expression Trees in C#

I continued work on the library that was mentioned by Serializing and Deserializing Expression Trees in C#

It looks like the project was abandoned (2008) but I did some work on it and now it works with .NET 4.0 and Silverlight. I made bug fixes to their code and also made it more DAL-independent.

http://expressiontree.codeplex.com/

Serialize expression tree

My Solution:

After putting the issue to rest for a long time a finally managed to solve my problem using json.net and Aq.ExpressionJsonSerializer (https://github.com/aquilae/expression-json-serializer)

public class JsonNetAdapter : IOconSerializer
{
private readonly JsonSerializerSettings _settings;

public JsonNetAdapter(JsonSerializerSettings settings = null)
{
var defaultSettings = new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.Objects};
defaultSettings.Converters.Add(new ExpressionJsonConverter(Assembly.GetAssembly(typeof(IOconSituation))));
_settings = settings ?? defaultSettings;
}

public string Serialize<T>(T obj)
{
return JsonConvert.SerializeObject(obj, _settings);
}

public T Deserialize<T>(string json)
{
return JsonConvert.DeserializeObject<T>(json, _settings);
}
}

Works like a charm!

Serialize LambdaExpression to and from string for saving in a database

Expressions are not serialisable. However, there are some third-part tools that may help.

I would recommend looking at Serialize.Linq. It is up to date, maintained, has a healthy number of downloads and will support .NET Framework 4.x as well as .NET Standard.

From the examples, it's pretty simple to use too:

Expression expression = Expression.Parameter(typeof(Person), "x");

// Serialize expression
var serializer = new ExpressionSerializer(new JsonSerializer());
string value = serializer.SerializeText(expression);
Console.WriteLine("value:" + value);

// Deserialize expression
var actualExpression = serializer.DeserializeText(value);
Console.WriteLine("actualExpression:" + actualExpression.ToJson());

Expression Tree Serializer

There's an implementation of IQueryable<T> in the framework - MSDN: EnumerableQuery<T>

If you can use this on the client to build the query, you can get the whole Expression Tree from the IQueryable<T>.Expression property.

You'll have to test this to see if it works with that Expression Tree Serializer.

var iQueryable = new EnumerableQuery<Model>( Enumerable.Empty<Model>() );

var query = iQueryable.Include( ... ).Where( ... ).OrderBy( ... );

var expressionTree = query.Expression;

You can then serialize the expression, squirt it accross the wire and then deserialize it.


Then the problem is that the expression tree is based on an EnumerableQuery<T>.

So you need to replace that with your IQueryable<T> source from your real DbContext

This gets a bit messy, but I've written an implementation using an ExpressionVisitor:

IQueryable FixupExpressionTree( ObjectContext ctx, Type entityType, Expression expression )
{
var tObjectContext = ctx.GetType();
var mCreateObjectSetOpen = tObjectContext.GetMethod( "CreateObjectSet", new Type[ 0 ] );
var mCreateObjectSetClosed = mCreateObjectSetOpen.MakeGenericMethod( entityType );

var objectQuery = ( ObjectQuery ) mCreateObjectSetClosed.Invoke( ctx, null );

var eFixed = new Visitor( objectQuery, entityType ).Visit( expression );

var qFixed = ( ( IQueryable ) objectQuery ).Provider.CreateQuery( eFixed );

return qFixed;
}

and the ExpressionVisitor itself:

public class Visitor : ExpressionVisitor
{
ObjectQuery _Source = null;
Type _EntityType = null;

public Visitor( ObjectQuery source, Type entityType ) { _Source = source; _EntityType = entityType; }

protected override Expression VisitConstant( ConstantExpression node )
{
if ( !node.Type.Name.Contains( "EnumerableQuery" ) ) return base.VisitConstant( node );

var eConstantInstance = Expression.Constant( _Source );
var eConstantArgument = Expression.Constant( MergeOption.AppendOnly );

var tObjectQueryOpen = typeof( ObjectQuery<> );
var tObjectQueryClosed = tObjectQueryOpen.MakeGenericType( _EntityType );
var eMergeAsMethod = tObjectQueryClosed.GetMethod( "MergeAs", BindingFlags.Instance | BindingFlags.NonPublic );

return Expression.Call( eConstantInstance, eMergeAsMethod, eConstantArgument );
}
}

Calling this is straight forward:

Type entityType = ...
Expression expression = ...
DbContext db = ...

ObjectContext ctx = ( ( IObjectContextAdapter ) db ).ObjectContext;

IQueryable query = FixupExpressionTree( ctx, entityType, expression );

Serialize Expression to access string

If you need to precisely serialize/deserialize the expression tree, Serialize.Linq library might help.

If all you want is some kind of string representation for display purposes, then I would recommend the ExpressionTreeToString library that I've written:

using ExpressionTreeToString;

Console.WriteLine(firstNameExpression.ToString("C#"));
/*
e => e.FirstName
*/

Console.WriteLine(firstNameExpression.ToString("Textual tree", "C#"));
/*
Lambda (Func<Employee, string>)
· Parameters[0] - Parameter (Employee) e
· Body - MemberAccess (string) FirstName
· Expression - Parameter (Employee) e
*/

There are various string representations available.

(Disclaimer: I am the author of the latter library.)

Compile Expression at runtime Using Serialize.Linq

You don't have to specify the generic type when calling ToExpression. Just test, if it returns an LambdaExpression.

public static async bool QueriesWouldContain<T>(IEnumerable<T> storables, List<string> queryStrings)
where T : class, IStorable
{

foreach (var querystring in queryStrings)
{
var expressionSerializer = new ExpressionSerializer(new JsonSerializer());
var queryStatement = expressionSerializer.DeserializeText(querystring);

var expression = queryStatement.ToExpressionNode().ToExpression();
if (!(expression is LambdaExpression lambdaExpression))
continue; // TODO: or throw
var d = lambdaExpression.Compile();
foreach (var storable in storables)
{
if (isOfTypeMatchingQuery(storable, d))
{
var result = (bool)d.Invoke(storable);

if (result == false)
{
return false;
}
}
}
return true;
}
}

Expression Trees in String format?

As per Serializing and Deserializing Expression Trees in C#, you could use the Expression Tree Serialization project.

Casting/converting an expression back to a lambda of Func Foo,bool (after serialization)

I figured it out. I was trying to do too much, I didn't need the Parameter expressions.

Just this after deserialization:

var barkFunction = ((Expression<Func<Foo, bool>>)barkExpression).Compile()


Related Topics



Leave a reply



Submit