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
How to Update Record Using Entity Framework 6
How to Rotate a Picture in Winforms
Using Stringwriter for Xml Serialization
Why Can't I Have Abstract Static Methods in C#
How to Get the Title of the Current Active Window Using C#
ASP.NET Calling Webmethod with Jquery Ajax "401 (Unauthorized)"
Order of Regular Expression Operator (..|.. ... ..|..)
Notify Observablecollection When Item Changes
Way to Have String.Replace Only Hit "Whole Words"
How to Compile and Execute New Code at Runtime in .Net
C# Get Thumbnail from File via Windows API
Calling C# Code from C++, But Executeindefaultappdomain() Is Too Limited
Executenonquery: Connection Property Has Not Been Initialized