PredicateMapper - Converting Expression Trees

Tags: Linq2Rest, Expression Trees

Recently I had a use case where I wanted to expose some data as a queryable service but did not want to expose the internal data model externally. This is essentially a problem of not leaking your internal data model to your external consumers.

One way is to keep a separate dataset for internal and external consumption, but that brings its own problems. A less invasive is to expose your external model and then translate query into your internal structure. As long as your external model is a subset of your internal model this is an easy thing to do using LINQ. Because queries are provided as expression trees you can analyse them and reason about the execution at runtime.

In the System.Linq.Expressions namespace you can find the ExpressionVisitor class which can be used to walk your expression tree and perform the conversion of the expression that you need in your expression tree. Assuming that the source and target types are reasonably similar it is easy to perform the substitution when walking the tree. I looked around, but there was no existing predicate mapper, so I created my own implementation and tests.

The basic API is like this:

var converter = PredicateMapper.Map<InDto, OutDto>()
	.And<ChildInDto, ChildOutDto>();

Expression<Func<InDto, bool>> expression = x => x.Children.Any(y => y.Value == "foo");

Expression<Func<OutDto, bool>> converted = converter.Convert<InDto, OutDto>(expression);

By default the mapper maps to a member of the same type and name. If the in and out types are not exactly the same (which is the reason for the mapping in the first place), then you can define specific member mappings, which will used when converting.

var converter = PredicateMapper.Map<InDto, AliasDto>()
.MapMember<InDto, AliasDto, string>(x => x.Name, x => x.Alias);

Expression<Func<InDto, bool>> expression = x => x.Name == "foo";

Expression<Func<AliasDto, bool>> converted = converter.Convert<InDto, AliasDto>(expression);

This isn’t strictly part of the core Linq2Rest API, but I think it is a useful way to abstract the internal model from the public model, so I have included it in the code base for version 4.

If you have comments to the mapper, then please submit them directly in the code.

Latest Tweets