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.
Related
I am defining a collection of mappings in code at design time and then executing those mappings at run time after doing some data extraction :
public class FormExtractionMap<T>
{
public Expression<Func<T>> Destination { get; set; }
public string Source { get; set; }
}
Mapping Code:
var extractionRequest = new ExtractionRequest<PlanningApplication>
{
Mapping = new List<FormExtractionMap<PlanningApplication>>
{
new FormExtractionMap<PlanningApplication> {Destination = x => x.Site.Address.MapCoordinate.Eastings, Source = "site_address_easting"},
new FormExtractionMap<PlanningApplication> {Destination = x => x.Site.Address.MapCoordinate.Northings, Source = "site_address_northing"},
}
};
and then I'm looking through each of the mapping expressions to go and get the Source value (any Type) and then assign it to the Destination Expression (any Type).
foreach (var extractionMap in extractionRequest.Mapping)
{
extractionRequest.ExtractTo.Set(extractionMap.Destination, form.GetValue(extractionMap.Source));
}
I then have Expression extensions to create a setter, compile and do the property assignment.
public static TEntity Set<TEntity, TProperty>(
this TEntity obj,
Expression<Func<TEntity, TProperty>> selector,
TProperty value)
{
var setterExpr = CreateSetter(selector);
setterExpr.Compile()(obj, value);
return obj;
}
private static Expression<Action<TEntity, TProperty>> CreateSetter<TEntity, TProperty>(Expression<Func<TEntity, TProperty>> selector)
{
ParameterExpression valueParameterExpression = Expression.Parameter(typeof(TProperty), "value");
Expression targetExpression = selector.Body is UnaryExpression ? ((UnaryExpression)selector.Body).Operand : selector.Body;
var newValue = Expression.Parameter(selector.Body.Type);
return Expression.Lambda<Action<TEntity, TProperty>>
(
Expression.Assign(targetExpression, Expression.Convert(valueParameterExpression, targetExpression.Type)),
selector.Parameters.Single(),
valueParameterExpression
);
}
if Source is a string and the Destination is a string the value is assigned fine. If the Destination is a double on setterExpr.Compile()(obj, value); I get :
System.InvalidCastException : Unable to cast object of type
'System.String' to type 'System.Double'.
I thought the "Expression.Convert" was handling the type conversion but clearly not. What am I doing wrong please ?
So, I did eventually solve this. Expression.Convert is akin to an explicit cast (Foo)Bar, not as "Convert" implies. I ended up with:
private static Expression<Action<TEntity, TProperty>> CreateSetter<TEntity, TProperty>(
Expression<Func<TEntity, TProperty>> selector, Type valueParameterType)
{
ParameterExpression valueParameterExpression = Expression.Parameter(typeof(object), "value");
Expression targetExpression = selector.Body is UnaryExpression ? ((UnaryExpression)selector.Body).Operand : selector.Body;
var resultBody = ConvertToDestination(valueParameterExpression, valueParameterType, targetExpression);
return Expression.Lambda<Action<TEntity, TProperty>>
(
Expression.Assign(targetExpression, resultBody),
selector.Parameters.Single(),
valueParameterExpression
);
}
private static Expression ConvertToDestination(ParameterExpression valueParameterExpression, Type valueParameterType, Expression targetExpression)
{
if (valueParameterType == typeof(string))
{
switch (targetExpression.Type)
{
case Type _ when targetExpression.Type == typeof(double):
return Expression.Call(typeof(Convert), "ToDouble", null, valueParameterExpression);
case Type _ when targetExpression.Type == typeof(int):
return Expression.Call(typeof(Convert), "ToInt", null, valueParameterExpression);
default:
return Expression.Convert(valueParameterExpression, targetExpression.Type);
}
}
return Expression.Convert(valueParameterExpression, targetExpression.Type);
}
However, I thought it was messy, verbose and frankly unnecessary as I was able to implement similar functionality using AutoMapper in a few hours. Automapper does a better job of type conversion, caching the maps etc. So the real solution was re-factor and don't re-invent the wheel.
I am looking to use CsvHelper dynamically by building up Expressions in code which represent property member access for a given type.
The method I am trying to pass these expressions to has the following signature:
public virtual CsvPropertyMap<TClass, TProperty> Map<TProperty>( Expression<Func<TClass, TProperty>> expression )
{
//
}
So you would normally call it, for any given type you want to map, like this (for a type with a property called 'stringProperty'):
mapper.Map(x => x.StringProperty);
Passing in a lambda which is converted internally into an Expression<Func<T, object>>
I have tried to create this expression in code, using Expressions. At compile time it all works fine (in that it returns an Expression<Func<TModel, object>>), but at runtime I get an exception 'not a member access'. Here is the code which takes a PropertyInfo object representing the property I want to map:
private Expression<Func<TModel, object>> CreateGetterExpression( PropertyInfo propertyInfo )
{
var getter = propertyInfo.GetGetMethod();
Expression<Func<TModel, object>> expression = m => getter.Invoke( m, new object[] { } );
return expression;
}
Basically, how do I build that Expression up properly in code?
Just try something looks like this:
public static Expression<Func<T, P>> GetGetter<T, P>(string propName)
{
var parameter = Expression.Parameter(typeof(T));
var property = Expression.Property(parameter, propName);
return Expression.Lambda<Func<T, P>>(property, parameter);
}
public static Expression<Func<T, P>> GetGetter<T, P>(PropertyInfo propInfo)
{
var parameter = Expression.Parameter(typeof(T));
var property = Expression.Property(parameter, propInfo);
return Expression.Lambda<Func<T, P>>(property, parameter);
}
It's example of usages:
private class TestCalss
{
public int Id { get; set; }
}
private static void Main(string[] args)
{
var getter = GetGetter<TestCalss, int>(typeof(TestCalss).GetProperty("Id")).Compile();
Console.WriteLine(getter(new TestCalss { Id = 16 }));
}
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);
}
}
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.
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.