Remove part of Expression - c#

I have a generic method:
public void ExpressionBuilder<T>() where T: IEntity
{
Expression<Func<T, long>> expr = e => e.Id; //Id is part of IEntity
...
}
The resulted expression contains casting of T to IEntity: e => Convert(e).Id
I want to remove this casting (to make it possible for EF to generate SQL). In other words I would like to replace Convert(e) by e.

If we assume a public long Id {get;} on T, and the expression is that simple, then maybe just build it manually:
var param = Expression.Parameter(typeof (T), "e");
var lambda = Expression.Lambda<Func<T, long>>(
Expression.PropertyOrField(param,"Id"), param);
A visitor should work for more complex examples; for example:
internal class FlattenInterface<T> : ExpressionVisitor
{
protected override Expression VisitMember(MemberExpression node)
{
if(node.Member.DeclaringType == typeof(T))
{
return Expression.MakeMemberAccess(
node.Expression,
node.Expression.Type.GetMember(node.Member.Name).Single());
}
return base.VisitMember(node);
}
}
and
var direct = (Expression<Func<T, long>>)new FlattenInterface<IEntity>()
.Visit(expr);

I have done it by ExpressionVisitor:
expr = ExpressionVisitor<UnaryExpression>.Visit(expr, p => return p.Operand);
ExpressionVisitor<T> is just an convenient wrapper for ExpressionVisitor.
If you know better ways - please post it.

Related

C# Expression.Property of an interface

I was generating a dynamic expression on CustomType based on some parameters. Code looks like this:
ParameterExpression parameter = Expression.Parameter(typeof(CustomType), "x");
MemberExpression idMember = Expression.Property(parameter, "CustomProperty");
When I changed from type CustomType to interface ICustomType it stopped working by throwing an error 'Instance property 'CustomProperty' is not defined for type 'ICustomType''
. How to fix it?
Without a minimum verifiable example, we cannot be sure what the problem is, but from your example code, I've put together the following:
interface ICustomType
{
int CustomProperty { get; set; }
}
class CustomType : ICustomType
{
public int CustomProperty { get; set; }
}
Now, when I call your example code, everything works as expected
ParameterExpression parameter = Expression.Parameter(typeof(CustomType), "x");
MemberExpression idMember = Expression.Property(parameter, "CustomProperty");
Also, when I change the type to ICustomType, it still works as expected.
ParameterExpression parameter = Expression.Parameter(typeof(ICustomType), "x");
MemberExpression idMember = Expression.Property(parameter, "CustomProperty");
However, if I remove the declaration of CustomProperty from ICustomType, I get the following error:
Instance property 'CustomProperty' is not defined for type 'ICustomType'
So, this leads me to believe that your interface does not include a declaration for CustomProperty. If you add it to your interface, your code should then work.
A common task when working with expressions is to replace certain nodes with other nodes. For instance, you can replace ParameterExpression as in this answer. I believe the OP used a similar parameter replacer, and forgot to also replace MemberExpression.
If you replace parameters of an expression, the original MemberExpression might not the new parameter type. E.g. a member expression for CustomType.CustomProperty will not be able to handle ICustomType.CustomProperty.
If my theory is correct, the OP must replace some MemberExpression instances too. The following expression visitor will do the trick:
public class ParameterReplacerVisitor : ExpressionVisitor
{
private readonly Type newType;
private Dictionary<ParameterExpression, ParameterExpression> parametersToReplace;
public ParameterReplacerVisitor(Type newType)
{
this.newType = newType;
}
public LambdaExpression Convert(LambdaExpression expression)
{
parametersToReplace = expression.Parameters
.Where(p => ShouldReplace(p.Type))
.ToDictionary(p => p, p => Expression.Parameter(newType, p.Name));
return (LambdaExpression)Visit(expression);
}
protected override Expression VisitLambda<T>(Expression<T> node)
{
var parameters = node.Parameters.Select(GetNewParameter);
return Expression.Lambda(Visit(node.Body), parameters);
}
protected override Expression VisitParameter(ParameterExpression node)
{
return base.VisitParameter(GetNewParameter(node));
}
protected override Expression VisitMember(MemberExpression node)
{
if (ShouldReplace(node.Member.DeclaringType))
{
var targetProperty = GetNewProperty(node.Member);
node = Expression.MakeMemberAccess(Visit(node.Expression), targetProperty);
}
return base.VisitMember(node);
}
private MemberInfo GetNewProperty(MemberInfo member)
{
return newType.GetProperty(member.Name) ?? throw new ArgumentException(
$"Property '{member.Name}' is not defined for type '{newType.Name}'"
);
}
private bool ShouldReplace(Type type) => newType.IsAssignableFrom(type);
private ParameterExpression GetNewParameter(ParameterExpression parameter)
{
parametersToReplace.TryGetValue(parameter, out var newParameter);
return newParameter ?? parameter;
}
}
Example
Expression<Func<Derived, string>> derived = t => t.A;
var lessDerived = derived.ToLessDerived<Derived, IBase, string>();
var d = lessDerived.Compile();
var result = d.Invoke(new Base());
And the extension method:
public static Expression<Func<TLess, TValue>> ToLessDerived<TClass, TLess, TValue>(this Expression<Func<TClass, TValue>> expression)
{
var visitor = new ParameterReplacerVisitor(typeof(TLess));
return (Expression<Func<TLess, TValue>>)visitor.Convert(expression);
}
For me, this solved the exact same type of error as the OP asked about.

How to pass Func to a class which encapsulates the Expression based on that Func?

I have class DropdownFilter which has:
private readonly Func<TEntity, string> fieldWhichMustEqualValue;
public override IQueryable<TEntity> Filter(IQueryable<TEntity> filteredEntityCollection, string value)
{
return filteredEntityCollection.Where(entity => this.fieldWhichMustEqualValue(entity) == value);
}
I use it as:
IQueryable<Invoice> entityCollectionToFilterAndOrder = ...
var dropdownFilter = new DropdownFilter<Invoice>(invoice => invoice.SomeProperty);
entityCollectionToFilterAndOrder = dropdownFilter.Filter(entityCollectionToFilterAndOrder, "bla bla bla");
which gives me
The LINQ expression node type 'Invoke' is not supported in LINQ to
Entities.
I understand the issue is that I am essentially asking for SQL equivalent of Invoke, which of course is wrong.
How should I rewrite the code? I understand that it needs to be an expression. My goal is the consumer of the DropDownFilter to just specify a property of TEntity, without providing the expression. i.e. the expression has to be encapsulated into the filter.
I have tried:
Expression<Func<TEntity, string>> expr = mc => this.fieldWhichMustEqualValue(mc);
Expression le = Expression.Equal(expr.Body, Expression.Constant(value));
var lambda = Expression.Lambda<Func<TEntity, bool>>(le, expr.Parameters);
return filteredEntityCollection.Where(lambda);
but it basically gives me the same result.
Func<TEntity, string> is a C# function so that cannot possibly be executed in the SQL server. However, if you change that to an expression, it could work:
private readonly Expression<Func<TEntity, string>> fieldWhichMustEqualValue;
However, now the Filter function would not compile since you can't invoke expressions. What you need to do is to compose a new expression:
public static UnaryExpression WrapInPropertyAccess(this ConstantExpression constant) {
Tuple<object> container = new Tuple<object>(constant.Value);
Expression containerExpression = Expression.Constant(container, typeof(Tuple<object>));
MemberExpression propAccess = Expression.Property(containerExpression, "Item1");
UnaryExpression result = Expression.Convert(propAccess, constant.Type); // Cast back the object to the value type
return result;
}
private Expression<TEntity, bool> FieldEqualsValue(string value) {
var comparison = Expression.Equal(fieldWhichMustEqualValue.Body, Expression.Constant(value).WrapInPropertyAccess());
return Expression.Lambda<Func<TEntity, bool>>(comparison, fieldWhichMustEqualValue.Parameters);
}
public override IQueryable<TEntity> Filter(IQueryable<TEntity> filteredEntityCollection, string value)
{
var condition = FieldEqualsValue(value);
return filteredEntityCollection.Where(condition);
}
The WrapInPropertyAccess function is not strictly needed, but using it makes Entity Framework generate a parameterized expression.

Using a LINQ ExpressionVisitor to replace primitive parameters with property references in a lambda expression

I'm in the process of writing a data layer for a part of our system which logs information about automated jobs that run every day - name of the job, how long it ran, what the result was, etc.
I'm talking to the database using Entity Framework, but I'm trying to keep those details hidden from higher-level modules and I don't want the entity objects themselves to be exposed.
However, I would like to make my interface very flexible in the criteria it uses to look up job information. For example, a user interface should allow the user to execute complex queries like "give me all jobs named 'hello' which ran between 10:00am and 11:00am that failed." Obviously, this looks like a job for dynamically-built Expression trees.
So what I'd like my data layer (repository) to be able to do is accept LINQ expressions of type Expression<Func<string, DateTime, ResultCode, long, bool>> (lambda expression) and then behind the scenes convert that lambda to an expression that my Entity Framework ObjectContext can use as a filter inside a Where() clause.
In a nutshell, I'm trying to convert a lambda expression of type Expression<Func<string, DateTime, ResultCode, long, bool>> to Expression<Func<svc_JobAudit, bool>>, where svc_JobAudit is the Entity Framework data object which corresponds to the table where job information is stored. (The four parameters in the first delegate correspond to the name of the job, when it ran, the result, and how long it took in MS, respectively)
I was making very good progress using the ExpressionVisitor class until I hit a brick wall and received an InvalidOperationException with this error message:
When called from 'VisitLambda', rewriting a node of type
'System.Linq.Expressions.ParameterExpression' must return a non-null
value of the same type. Alternatively, override 'VisitLambda' and
change it to not visit children of this type.
I'm completely baffled. Why the heck won't it allow me to convert expression nodes which reference parameters to nodes which reference properties? Is there another way to go about this?
Here is some sample code:
namespace ExpressionTest
{
class Program
{
static void Main(string[] args)
{
Expression<Func<string, DateTime, ResultCode, long, bool>> expression = (myString, myDateTime, myResultCode, myTimeSpan) => myResultCode == ResultCode.Failed && myString == "hello";
var result = ConvertExpression(expression);
}
private static Expression<Func<svc_JobAudit, bool>> ConvertExpression(Expression<Func<string, DateTime, ResultCode, long, bool>> expression)
{
var newExpression = Expression.Lambda<Func<svc_JobAudit, bool>>(new ReplaceVisitor().Modify(expression), Expression.Parameter(typeof(svc_JobAudit)));
return newExpression;
}
}
class ReplaceVisitor : ExpressionVisitor
{
public Expression Modify(Expression expression)
{
return Visit(expression);
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (node.Type == typeof(string))
{
return Expression.Property(Expression.Parameter(typeof(svc_JobAudit)), "JobName");
}
return node;
}
}
}
The problem was two-fold:
I was misunderstanding how to visit the Lambda expression type. I was still returning a lambda which matched the old delegate instead of returning a new lambda to match the new delegate.
I needed to hold a reference to the new ParameterExpression instance, which I wasn't doing.
The new code looks like this (notice how the visitor now accepts a reference to a ParameterExpression matching the Entity Framework data object):
class Program
{
const string conString = #"myDB";
static void Main(string[] args)
{
Expression<Func<string, DateTime, byte, long, bool>> expression = (jobName, ranAt, resultCode, elapsed) => jobName == "Email Notifications" && resultCode == (byte)ResultCode.Failed;
var criteria = ConvertExpression(expression);
using (MyDataContext dataContext = new MyDataContext(conString))
{
List<svc_JobAudit> jobs = dataContext.svc_JobAudit.Where(criteria).ToList();
}
}
private static Expression<Func<svc_JobAudit, bool>> ConvertExpression(Expression<Func<string, DateTime, byte, long, bool>> expression)
{
var jobAuditParameter = Expression.Parameter(typeof(svc_JobAudit), "jobAudit");
var newExpression = Expression.Lambda<Func<svc_JobAudit, bool>>(
new ReplaceVisitor()
.Modify(expression.Body, jobAuditParameter), jobAuditParameter);
return newExpression;
}
}
class ReplaceVisitor : ExpressionVisitor
{
private ParameterExpression parameter;
public Expression Modify(Expression expression, ParameterExpression parameter)
{
this.parameter = parameter;
return Visit(expression);
}
protected override Expression VisitLambda<T>(Expression<T> node)
{
return Expression.Lambda<Func<svc_JobAudit, bool>>(Visit(node.Body), Expression.Parameter(typeof(svc_JobAudit)));
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (node.Type == typeof(string))
{
return Expression.Property(parameter, "JobName");
}
else if (node.Type == typeof(DateTime))
{
return Expression.Property(parameter, "RanAt");
}
else if (node.Type == typeof(byte))
{
return Expression.Property(parameter, "Result");
}
else if (node.Type == typeof(long))
{
return Expression.Property(parameter, "Elapsed");
}
throw new InvalidOperationException();
}
}
The accepted answer is 'hardcoded' to some specific types. Here's a more general expression rewriter than can substitute a parameter for any other expression (lambda, constant, ...). In the case of a lambda expression the expression's signature needs to change to incorporate the parameters needed by the substituted value.
public class ExpressionParameterSubstitute : System.Linq.Expressions.ExpressionVisitor
{
private readonly ParameterExpression from;
private readonly Expression to;
public ExpressionParameterSubstitute(ParameterExpression from, Expression to)
{
this.from = from;
this.to = to;
}
protected override Expression VisitLambda<T>(Expression<T> node)
{
if (node.Parameters.All(p => p != this.from))
return node;
// We need to replace the `from` parameter, but in its place we need the `to` parameter(s)
// e.g. F<DateTime,Bool> subst F<Source,DateTime> => F<Source,bool>
// e.g. F<DateTime,Bool> subst F<Source1,Source2,DateTime> => F<Source1,Source2,bool>
var toLambda = to as LambdaExpression;
var substituteParameters = toLambda?.Parameters ?? Enumerable.Empty<ParameterExpression>();
ReadOnlyCollection<ParameterExpression> substitutedParameters
= new ReadOnlyCollection<ParameterExpression>(node.Parameters
.SelectMany(p => p == this.from ? substituteParameters : Enumerable.Repeat(p, 1) )
.ToList());
var updatedBody = this.Visit(node.Body); // which will convert parameters to 'to'
return Expression.Lambda(updatedBody, substitutedParameters);
}
protected override Expression VisitParameter(ParameterExpression node)
{
var toLambda = to as LambdaExpression;
if (node == from) return toLambda?.Body ?? to;
return base.VisitParameter(node);
}
}

Create dynamic Expression lambda from two others (chaining the Expressions)

Given a lambda that takes an Identification object, and returns a property:
Expression<Func<Identification, object>> fx = _ => _.Id;
And a conversion lambda that converts an object into an Identification instance:
ParameterExpression p = Expression.Parameter(typeof(object), "o");
Expression #new = Expression.Lambda(Expression.Convert(p, typeof(Identification)), p);
How do I build a new lambda that executes #new (getting out the Identification Instance) and passes the result into fx. I need #new's result to bind to the first parameter of fx somehow, and I cannot find an example.
I need the result to be an Expression, it should be of type Expression<Func<object, object>> and it should convert the inbound parameter to an Identification and then get the Id property.
Firstly, note that this is easier if you type #new appropriately, i.e.:
LambdaExpression #new = ...
since that provides easy access to #new.Body and #new.Parameters; that done,
Expression.Invoke can be useful here:
var combinedParam = Expression.Parameter(typeof(object), "o");
var combined = Expression.Lambda<Func<object, object>>(
Expression.Invoke(fx,
Expression.Invoke(#new, combinedParam)), combinedParam);
although for a cleaner expression, you can also use ExpressionVisitor to completely replace the inner expressions:
var injected = new SwapVisitor(fx.Parameters[0], #new.Body).Visit(fx.Body);
var combined = Expression.Lambda<Func<object, object>>(injected,#new.Parameters);
with:
class SwapVisitor : ExpressionVisitor {
private readonly Expression from, to;
public SwapVisitor(Expression from, Expression to) {
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node) {
return node == from ? to : base.Visit(node);
}
}
what this does is:
inspect the fx.Body tree, replacing all instances of _ (the parameter) with the #new.Body (note that this will contain references to the o parameter (aka p)
we then build a new lambda from the replaced expression, re-using the same parameters from #new, which ensures that the values we injected will be bound correctly
Using the code from Marc Gravell's answer, you can simplify this really nicely with a helper function:
public static class ExpressionHelper {
public static Expression<Func<TFrom, TTo>> Chain<TFrom, TMiddle, TTo>(
this Expression<Func<TFrom, TMiddle>> first,
Expression<Func<TMiddle, TTo>> second
) {
return Expression.Lambda<Func<TFrom, TTo>>(
new SwapVisitor(second.Parameters[0], first.Body).Visit(second.Body),
first.Parameters
);
}
private class SwapVisitor : ExpressionVisitor {
private readonly Expression _from;
private readonly Expression _to;
public SwapVisitor(Expression from, Expression to) {
_from = from;
_to = to;
}
public override Expression Visit(Expression node) {
return node == _from ? _to : base.Visit(node);
}
}
}
Now look how clean that is!
var valueSelector = new Expression<Func<MyTable, int>>(o => o.Value);
var intSelector = new Expression<Func<int, bool>>(x => x > 5);
var selector = valueSelector.Chain<MyTable, int, bool>(intSelector);
And it works with Entity Framework and other things that need a clean Expression that doesn't try to invoke a Func within it.

AutoMapper for Func's between selector types

I have two types: Cat and Dog. I'd like to select Cats using a Func<Dog, bool>. To do that, I need a way to map the properties from Cat to Dog in some sort of mapper (similar to how AutoMapper maps properties from one object to another type of object).
I'm imagining something like this:
public Cat GetCat(Func<Dog, bool> selector)
{
Func<Cat, bool> mappedSelector = getMappedSelector(selector);
return _catRepository.Get(mappedSelector);
}
private Func<Cat, bool> getMappedSelector(Func<Dog, bool> selector)
{
//some code here to map from one function type to another
//something like AutoMapper would be sweet...
//something that I can configure how I want the properties to be mapped.
}
Either there's already something that does this or there should be.
Here's a solution using AutoMapper:
Func<Cat, bool> GetMappedSelector(Func<Dog, bool> selector)
{
Func<Cat, Dog> mapper = Mapper.CreateMapExpression<Cat, Dog>().Compile();
Func<Cat, bool> mappedSelector = cat => selector(mapper(cat));
return mappedSelector;
}
UPDATE: It's been 1.5 years since I first answered this, and I figured I'd expand on my answer now since people are asking how to do this when you have an expression as opposed to a delegate.
The solution is the same in principle - we need to be able to compose the two functions (selector and mapper) into a single function. Unfortunately, since there's no way in C# to "call" one expression from another (like we could with delegates), we can't directly represent this in code. For example, the following code will fail to compile:
Expression<Func<Cat, bool>> GetMappedSelector(Expression<Func<Dog, bool>> selector)
{
Expression<Func<Cat, Dog>> mapper = Mapper.CreateMapExpression<Cat, Dog>();
Expression<Func<Cat, bool>> mappedSelector = cat => selector(mapper(cat));
return mappedSelector;
}
The only way to create our composed function, therefore, is to build up the expression tree ourselves using the System.Linq.Expressions classes.
What we really need to do is to modify the body of the selector function so that all instances of its parameter are replaced by the body of the mapper function. This will become the body of our new function, which will accept mapper's parameter.
To replace the parameter I created a subclass of ExpressionVisitor class that can traverse an expression tree and replace a single parameter with an arbitrary expression:
class ParameterReplacer : ExpressionVisitor
{
private ParameterExpression _parameter;
private Expression _replacement;
private ParameterReplacer(ParameterExpression parameter, Expression replacement)
{
_parameter = parameter;
_replacement = replacement;
}
public static Expression Replace(Expression expression, ParameterExpression parameter, Expression replacement)
{
return new ParameterReplacer(parameter, replacement).Visit(expression);
}
protected override Expression VisitParameter(ParameterExpression parameter)
{
if (parameter == _parameter)
{
return _replacement;
}
return base.VisitParameter(parameter);
}
}
Then I created an extension method, Compose(), that uses the visitor to compose two lambda expressions, an outer and an inner:
public static class FunctionCompositionExtensions
{
public static Expression<Func<X, Y>> Compose<X, Y, Z>(this Expression<Func<Z, Y>> outer, Expression<Func<X, Z>> inner)
{
return Expression.Lambda<Func<X ,Y>>(
ParameterReplacer.Replace(outer.Body, outer.Parameters[0], inner.Body),
inner.Parameters[0]);
}
}
Now, with all that infrastructure in place, we can modify our GetMappedSelector() method to use our Compose() extension:
Expression<Func<Cat, bool>> GetMappedSelector(Expression<Func<Dog, bool>> selector)
{
Expression<Func<Cat, Dog>> mapper = Mapper.CreateMapExpression<Cat, Dog>();
Expression<Func<Cat, bool>> mappedSelector = selector.Compose(mapper);
return mappedSelector;
}
I created a simple console application to test this out. Hopefully, my explanation was not too obfuscated; but unfortunately, there isn't really a simpler approach to doing what you're trying to do. If you are still totally confused, at least you can reuse my code and have gained an appreciation for the nuances and complexities of dealing with expression trees!
#luksan, thanks for the inspiration! Your solution didn't solve my problem, but got me thinking. Since I needed to pass the translated expression to IQueryable.OrderBy(), using the inside-expression translation approach didn't work. But I came up with a solution that will work on both cases and is also simpler to implement. It's also generic so can be reused for any mapped types. Here is the code:
private Expression<Func<TDestination, TProperty>> GetMappedSelector<TSource, TDestination, TProperty>(Expression<Func<TSource, TProperty>> selector)
{
var map = Mapper.FindTypeMapFor<TSource, TDestination>();
var mInfo = ReflectionHelper.GetMemberInfo(selector);
if (mInfo == null)
{
throw new Exception(string.Format(
"Can't get PropertyMap. \"{0}\" is not a member expression", selector));
}
PropertyMap propmap = map
.GetPropertyMaps()
.SingleOrDefault(m =>
m.SourceMember != null &&
m.SourceMember.MetadataToken == mInfo.MetadataToken);
if (propmap == null)
{
throw new Exception(
string.Format(
"Can't map selector. Could not find a PropertyMap for {0}", selector.GetPropertyName()));
}
var param = Expression.Parameter(typeof(TDestination));
var body = Expression.MakeMemberAccess(param, propmap.DestinationProperty.MemberInfo);
var lambda = Expression.Lambda<Func<TDestination, TProperty>>(body, param);
return lambda;
}
Here is the ReflectionHelper code (used just to keep the code above cleaner)
private static class ReflectionHelper
{
public static MemberInfo GetMemberInfo(Expression memberExpression)
{
var memberExpr = memberExpression as MemberExpression;
if (memberExpr == null && memberExpression is LambdaExpression)
{
memberExpr = (memberExpression as LambdaExpression).Body as MemberExpression;
}
return memberExpr != null ? memberExpr.Member : null;
}
}

Categories