Building Expression Trees and using Expression.Call method - c#

I have two dummy classes named TClass1 and TClass2. I would like to know how to build an expression tree to call the operation TClass1.TestMethod. I specifically have problem using Expression.Call method to create an expression based on instance methods of a class. Any help would be appreciated.
public class TClass1
{
public string Prop1 { get; set; }
public int Prop2 { get; set; }
public TClass2 TestMethod(TClass2 tc2, int c)
{
return new TClass2() { Cprop1 = "The result: " + this.Prop1 + ".", Cprop2 = this.Prop2 * c };
}
}
public class TClass2
{
public string Cprop1 { get; set; }
public int Cprop2 { get; set; }
}

Try this code:
var par1 = Expression.Parameter(typeof(TClass2), "tc2");
var par2 = Expression.Parameter(typeof(int), "c");
var instance = Expression.Parameter(typeof(TClass1), "inst");
var inExp = Expression.Call(instance,typeof(TClass1).GetMethod("TestMethod"),par1,par2);
var exp = Expression.Lambda<Func<TClass1,TClass2,int,TClass2>>(inExp,instance,
par1,par2);
var deleg = exp.Compile();

Related

How to generic Expression with "where" clause?

I want a entity that filters the list entity within the entity.
I have entities with common structure like below
public class FoodMenuT
{
public int Id {get;set;}
public int LanguageId {get;set;}
public string Name {get;set;}
}
public class FoodMenu
{
public int Id {get;set;}
public IEnumerable<FoodMenuT> FoodMenuTList {get;set;}
}
public static Expression<Func<FoodMenu, IEnumerable<FoodMenuT>>> LanguageFilter()
{
return e => e.FoodMenuT.Where(x => x.LanguageId == 1);
}
What I want to do is to do this in a generic way.
public static Expression<Func<T, IEnumerable<TT>>> LanguageFilter<T, TT>()
{
}
Thank you in advance for your helps.
public static Expression<Func<T, IEnumerable<TT>>> LanguageFilter<T, TT>()
{
var headerInfo = ServiceTool.ServiceProvider.GetService<IHeaderInfo>();
var entity = typeof(T);
var ttEntity = typeof(TT);
var propertyInfo = entity.GetProperty(ttEntity.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
var arg = Expression.Parameter(entity, "e");
var property = Expression.Property(arg, propertyInfo!.Name);
//e=>e.FoodMenuT
var selector = Expression.Lambda(property, new ParameterExpression[] { arg });
var propertyInfo2 = ttEntity.GetProperty("LanguageId", BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
var arg2 = Expression.Parameter(ttEntity, "x");
var property2 = Expression.Property(arg2, propertyInfo2!.Name);
var languageId = Expression.Constant(headerInfo!.LanguageId);
var equalLanguage = Expression.Equal(property2, languageId);
//x=>x.LanguageId==1
var selector2 = Expression.Lambda<Func<TT, bool>>(equalLanguage, arg2);
I couldn't combine "selector" and "selector2" with "where".
var lambda = Expression.Lambda<Func<T, IEnumerable<TT>>>(null, arg);
return lambda;
}
I solved the problem with my own means
public interface ITEntity<TT>
{
public List<TT> Translate { get; set; }
}
public interface ILEntity
{
public int LanguageId { get; set; }
}
public class FoodMenu : ITEntity<FoodMenuT>
{
public virtual List<FoodMenuT> Translate { get; set; } = new();
}
public class FoodMenuT : ILEntity
{
public int LanguageId { get; set; }
}
public static Expression<Func<T, IEnumerable<TT>>> LanguageFilter<T, TT>() where T : ITEntity<TT> where TT : ILEntity
{
return e => e.Translate.Where(x => x.LanguageId == 1);
}

map common properties in separate method using generics

I have a class MechanicalData and in that i have list of objects and in the below function i am trying to form list of objects with values coming from input.
Mechanicaldata class looks like below
public class MechanicalData
{
public List<LibraryA170> AirflowsA170 { get; set; }
......
}
and the classes libraryA170 and LibraryAcoustic looks like as follows
public class LibraryA170 : AEIMasterBase
{
public string Category { get; set; }
public string SpaceFunction { get; set; }
[Column(TypeName = "varchar(32)")]
public DirectExhaust? DirectExhaust { get; set; }
.....
......
}
public class LibraryAcoustic : AEIMasterBase
{
public string Category { get; set; }
public string SpaceFunction { get; set; }
public double? NoiseCriteria { get; set; }
.......
}
and base class AEIMasterBase looks like as below
public class AEIMasterBase
{
public Guid Id { get; set; }
public MasterSection MasterSection { get; set; }
public List<string> NotesHTML { get; set; }
public bool? IsApproved { get; set; }
public Guid? InitialRevisionId { get; set; }
public Guid? LatestRevisionId { get; set; }
public int? Revision { get; set; }
}
Below i am trying to map all those fields with LINQ select
private static MechanicalData TransformedMechanicalData(MechanicalData sourceMechanicalData, Dictionary<string, MasterSection> masterSectionMappedLibrary)
{
return new MechanicalData
{
AirflowsA170 = sourceMechanicalData.AirflowsA170.Select(airflow170 => new LibraryA170
{
Id = airflow170.Id,
InitialRevisionId = airflow170.InitialRevisionId,
LatestRevisionId = airflow170.LatestRevisionId,
IsApproved = true,
Revision = airflow170.Revision,
NotesHTML = airflow170.NotesHTML,
SpaceFunction = airflow170.SpaceFunction,
Category = airflow170.Category,
MasterSection = masterSectionMappedLibrary["Library A170"],
........
}).ToList(),
Acoustic = sourceMechanicalData.Acoustic.Select(acoustic => new LibraryAcoustic
{
Id = acoustic.Id,
InitialRevisionId = acoustic.InitialRevisionId,
LatestRevisionId = acoustic.LatestRevisionId,
IsApproved = true,
Revision = acoustic.Revision,
NotesHTML = acoustic.NotesHTML,
Category = acoustic.Category,
SpaceFunction = acoustic.SpaceFunction,
......
}).ToList()
};
}
is there any way i can pass two objects to a method and map common properties inside that method and leave uncommon properties to be mapped inside the select statement while adding to the list.
I am looking for something like as below if possible
public static class ItemExtensionMethods
{
public static readonly Expression<Func<Item, MinimalItem>> MapToMinimalItemExpr =
source => new MinimalItem
{
Id = source.Id, // only common properties like id, revision, IsApproved
Property1 = source.Property1
};
}
below are some common properties
Id = airflow170.Id,
InitialRevisionId = airflow170.InitialRevisionId,
LatestRevisionId = airflow170.LatestRevisionId,
.......
Could any one please suggest any idea on this, Many thanks in advance
update getting below error
Given two expression tree's, you want to locate the two MemberInitExpression nodes, merge their MemberBinding's and swap any ParameterExpression.
class LocateBindings : ExpressionVisitor
{
public IEnumerable<MemberBinding> Bindings { get; private set; }
protected override Expression VisitMemberInit(MemberInitExpression node)
{
Bindings = node.Bindings;
return base.VisitMemberInit(node);
}
}
class MergeBindings : ExpressionVisitor
{
private IEnumerable<MemberBinding> bindings;
private ParameterExpression parameter;
public MergeBindings(IEnumerable<MemberBinding> bindings, ParameterExpression parameter)
{
this.bindings = bindings;
this.parameter = parameter;
}
protected override Expression VisitMemberInit(MemberInitExpression node)
=> node.Update(node.NewExpression,
node.Bindings.Concat(bindings)
.Select(VisitMemberBinding));
protected override Expression VisitParameter(ParameterExpression node)
=> parameter;
}
public static Expression<Func<P, D>> Merge<BP, P, B, D>(
Expression<Func<BP, B>> baseExpr,
Expression<Func<P, D>> derivedExpr
)
where D:B where P:BP
{
var locate = new LocateBindings();
locate.Visit(baseExpr);
var merge = new MergeBindings(locate.Bindings, derivedExpr.Parameters[0]);
return merge.VisitAndConvert(derivedExpr, "");
}
For example;
Expression<Func<[insert type], AEIMasterBase>> baseExpression = basearg => new AEIMasterBase
{
Id = basearg.Id,
InitialRevisionId = basearg.InitialRevisionId,
LatestRevisionId = basearg.LatestRevisionId,
IsApproved = true,
Revision = basearg.Revision,
NotesHTML = basearg.NotesHTML,
SpaceFunction = basearg.SpaceFunction,
};
Expression<Func<[insert type], LibraryA170>> derivedExpression = airflow170 => new LibraryA170
{
Category = airflow170.Category,
MasterSection = masterSectionMappedLibrary["Library A170"],
};
var merged = Merge(baseExpression, derivedExpression);
...
AirflowsA170 = sourceMechanicalData.AirflowsA170.Select(merged).ToList()
...

Convert strongly typed Expression to anonymous type return

I'm trying to dynamically convert this type of expression :
myDbSet.Select(x => new MyClass
{
IsSelected = x.ChildList.Any()
})
into :
myDbSet.Select(x => new
{
Item = x
IsSelected = x.ChildList.Any()
})
I would write an extension method that would take the strongly typed version and transform into the anonymous then create a new expression in order to do something like this :
myDbSet.Select(anonymousTransformedExpression).ToList().
.Select(newGeneratedExpression)
I would want this newGeneratedExpression to be:
myDbSet.Select(x => new
{
x.Item.IsSelected = x.IsSelected
return x.Item;
})
So basically tranform the return back into a strongly typed but with the "IsSelected" value applied.
Problem is I cant really find a starting point on how to do so..
EDIT
Alright I realised that the question wasn't so clear and I've come a little closer to the solution with one main problem so far. Here's what I got :
public static IEnumerable<TModel> SelectWithUnmapped<TModel> (this IQueryable<TModel> source, Expression<Func<TModel, object>> assigner) where TModel : class, new()
{
var anonymousType = typeof(AnonymousType<TModel>);
var expression = Expression.New(anonymousType);
var parameter = Expression.Parameter(typeof(TModel), "x");
//this part is hard coded to only take binding at position 0.. eventually will become dynamic
var originalBinding = ((MemberAssignment) ((MemberInitExpression) assigner.Body).Bindings[0]);
var originalExpression = originalBinding.Expression;
Expression conversion = Expression.Convert(originalExpression, typeof(object));
var bindings = new[]
{
Expression.Bind(anonymousType.GetProperty("Item"), parameter),
//this is hardcoded test
Expression.Bind(anonymousType.GetProperty("Property1"), conversion)
};
var body = Expression.MemberInit(expression, bindings);
var lambda = Expression.Lambda<Func<TModel, AnonymousType<TModel>>>(body, parameter);
var test = source.Select(lambda).ToList();
return source;
}
class AnonymousType<TModel>
{
public TModel Item { get; set; }
public object Property1 { get; set; }
public object Property2 { get; set; }
public object Property3 { get; set; }
public object Property4 { get; set; }
public object Property5 { get; set; }
public object Property6 { get; set; }
public object Property7 { get; set; }
public object Property8 { get; set; }
public object Property9 { get; set; }
public object Property10 { get; set; }
public object Property11 { get; set; }
public object Property12 { get; set; }
public object Property13 { get; set; }
public object Property14 { get; set; }
public object Property15 { get; set; }
public object Property16 { get; set; }
public object Property17 { get; set; }
public object Property18 { get; set; }
public object Property19 { get; set; }
public object Property20 { get; set; }
}
}
When I try to assign test, I get the following error : The parameter 'x' was not bound in the specified LINQ to Entities query expression.
This is due to my second binding using the original expression's binding expression. However both use the same parameter name of "x". How could I make sure my new binding actually know that x is the parameter from the new expression?
So basically so far I'm trying to take in an expression that looks like :
x => new Test
{
PropertyTest = x.Blah.Count()
}
into :
x => new AnonymousType<Test>(){
Item = x,
Property1 = x.Blah.Count()
}
I finally finished it up. I guess it just took patience and trial and error a lot. To help anyone who would like to do the same. I had to limit myself to a certain property count.. which can be added easily.
Here's the code :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
public static class IQueryableExtensions
{
public static IEnumerable<TModel> SelectAndAssign<TModel>(this IQueryable<TModel> source, Expression<Func<TModel, object>> assigner) where TModel : class, new()
{
//get typed body of original expression
var originalBody = (MemberInitExpression)assigner.Body;
//list to store the new bindings we're creating for new expression
var newExpressionBindings = new List<MemberBinding>();
var newExpressionReturnType = typeof(AnonymousType<TModel>);
//new param
var parameter = Expression.Parameter(typeof(TModel), "x");
//base binding
newExpressionBindings.Add(Expression.Bind(newExpressionReturnType.GetProperty("Item"), parameter));
//go through all the original expression's bindings
for (var i = 0; i < originalBody.Bindings.Count; i++)
{
var originalBinding = (MemberAssignment)originalBody.Bindings[i];
var originalExpression = originalBinding.Expression;
var memberType = originalBinding.Expression.Type;
//create delegate based on the member type
var originalLambdaDelegate = typeof(Func<,>).MakeGenericType(typeof(TModel), memberType);
//create lambda from that delegate
var originalLambda = Expression.Lambda(originalLambdaDelegate, originalExpression, assigner.Parameters[0]);
//create a AnonymousVar<MemberType> from the type of the member ( to please EF unable to assign bool to object directly )
//start with getting the generic type
var genericMemberType = typeof(AnonymousVar<>).MakeGenericType(memberType);
//then create teh delegate
var genericMemberTypeDelegate = typeof(Func<>).MakeGenericType(genericMemberType);
//Now create an expression with a binding for that object to assign its Property ( strongly typed now from the generic declaration )
var genericInstantiationExpression = Expression.New(genericMemberType);
//the binding.. using the original expression expression
var genericInstantiationBinding = Expression.Bind(genericMemberType.GetProperty("Property"), originalLambda.Body);
// create the body
var genericInstantiationBody = Expression.MemberInit(genericInstantiationExpression, genericInstantiationBinding);
//now we need to recreate a lambda for this
var newBindingExpression = Expression.Lambda(genericMemberTypeDelegate, genericInstantiationBody);
//Create the binding and add it to the new expression bindings
newExpressionBindings.Add(Expression.Bind(newExpressionReturnType.GetProperty("Property" + (i + 1)), newBindingExpression.Body));
}
//start creating the new expression
var expression = Expression.New(newExpressionReturnType);
//create new expression body with bindings
var body = Expression.MemberInit(expression, newExpressionBindings);
//The actual new expression lambda
var newLambda = Expression.Lambda<Func<TModel, AnonymousType<TModel>>>(body, parameter);
// replace old lambda param with new one
var replacer = new ParameterReplacer(assigner.Parameters[0], newLambda.Parameters[0]); // replace old lambda param with new
//new lambda with fixed params
newLambda = Expression.Lambda<Func<TModel, AnonymousType<TModel>>>(replacer.Visit(newLambda.Body), newLambda.Parameters[0]);
//now that we have all we need form the server, we materialize the list
var materialized = source.Select(newLambda).ToList();
//typed return parameter
var typedReturnParameter = Expression.Parameter(typeof(AnonymousType<TModel>), "x");
//Lets assign all those custom properties back into the original object type
var expressionLines = new List<Expression>();
for (var i = 0; i < originalBody.Bindings.Count; i++)
{
var originalBinding = (MemberAssignment)originalBody.Bindings[i];
var itemPropertyExpression = Expression.Property(typedReturnParameter, "Item");
var bindingPropertyExpression = Expression.Property(itemPropertyExpression, originalBinding.Member.Name);
var memberType = originalBinding.Expression.Type;
var valuePropertyExpression = Expression.Convert(Expression.Property(typedReturnParameter, "Property" + (i + 1)), typeof(AnonymousVar<>).MakeGenericType(memberType));
var memberValuePropertyExpression = Expression.Property(valuePropertyExpression, "Property");
var equalExpression = Expression.Assign(bindingPropertyExpression, memberValuePropertyExpression);
expressionLines.Add(equalExpression);
}
var returnTarget = Expression.Label(typeof(TModel));
expressionLines.Add(Expression.Return(returnTarget, Expression.Property(typedReturnParameter, "Item")));
expressionLines.Add(Expression.Label(returnTarget, Expression.Constant(null, typeof(TModel))));
var finalExpression = Expression.Block(expressionLines);
var typedReturnLambda = Expression.Lambda<Func<AnonymousType<TModel>, TModel>>(finalExpression, typedReturnParameter).Compile();
return materialized.Select(typedReturnLambda);
}
class AnonymousVar<TModel>
{
public TModel Property { get; set; }
}
class AnonymousType<TModel>
{
public TModel Item { get; set; }
public object Property1 { get; set; }
public object Property2 { get; set; }
public object Property3 { get; set; }
public object Property4 { get; set; }
public object Property5 { get; set; }
public object Property6 { get; set; }
public object Property7 { get; set; }
public object Property8 { get; set; }
public object Property9 { get; set; }
public object Property10 { get; set; }
public object Property11 { get; set; }
public object Property12 { get; set; }
public object Property13 { get; set; }
public object Property14 { get; set; }
public object Property15 { get; set; }
public object Property16 { get; set; }
public object Property17 { get; set; }
public object Property18 { get; set; }
public object Property19 { get; set; }
public object Property20 { get; set; }
}
class ParameterReplacer : ExpressionVisitor
{
private ParameterExpression from;
private ParameterExpression to;
public ParameterReplacer(ParameterExpression from, ParameterExpression to)
{
this.from = from;
this.to = to;
}
protected override Expression VisitParameter(ParameterExpression node)
{
return base.VisitParameter(node == this.from ? this.to : node);
}
}
}

Expression.PropertyOrField returns "not a member of type 'System.String'" for string property

I'm trying to build expressions dynamically for a rules engine and things were going very well until I tried to allow nested types and properties to be specified as operands. Sample:
ExpressionBuilder
public Expression BuildExpression<T>(string propertyName, Enums.Operator ruleOperator, object value, ParameterExpression parameterExpression)
{
ExpressionType expressionType = new ExpressionType();
Expression body = parameterExpression;
foreach (var member in propertyName.Split('.'))
{
body = MemberExpression.Property(body, member);
}
var leftOperand = MemberExpression.PropertyOrField(body, propertyName);
var rightOperand = Expression.Constant(Convert.ChangeType(value, value.GetType()));
FieldInfo fieldInfo = expressionType.GetType().GetField(Enum.GetName(typeof(Enums.Operator), ruleOperator));
var expressionTypeValue = (ExpressionType)fieldInfo.GetValue(ruleOperator);
return CastBuildExpression(expressionTypeValue, value, leftOperand, rightOperand);
}
RuleEngine
public Func<T, bool>[] CombineRules<T>(Criterion[] criteria)
{
List<Func<T, bool>> list = new List<Func<T, bool>>();
foreach (var criterion in criteria)
{
ExpressionBuilder expressionBuilder = new ExpressionBuilder();
var param = Expression.Parameter(typeof (T));
Expression expression = expressionBuilder.BuildExpression<T>(criterion.PropertyName,
criterion.Operator_, criterion.Value, param);
Func<T, bool> func = Expression.Lambda<Func<T, bool>>(expression, param).Compile();
list.Add(func);
}
return list.ToArray();
}
Criterion
public class Criterion
{
private bool propertySet;
public string PropertyName { get; set; }
public Enums.Operator Operator_ { get; set; }
public object Value { get; set; }
MemberModel
public class MemberModel
{
public string UserName{ get; set; }
public PersonalDetailsModel PersonalDetails {get; set;}
}
PersonalDetailsModel
public class PersonalDetailsModel
{
public int PersonalDetailsId { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
public string Middlename { get; set; }
public string DateOfBirth { get; set; }
public string GenderType { get; set; }
public string SalutationType { get; set; }
}
The problem arises when I try to pass in nested properties as a left operand, i.e. PropertyName="PersonalDetails.FirstName" into ruleEngine.CombineRules(criteria.ToArray());
I get "'PersonalDetails.Firstname' not a member of type 'System.String'", despite it clearly being so. I've been stuck on this for a while now, any idea what might be causing it?
Any help would be greatly appreciated.
Here
Expression body = parameterExpression;
foreach (var member in propertyName.Split('.'))
{
body = MemberExpression.Property(body, member);
}
you already processed the property path, so this
var leftOperand = MemberExpression.PropertyOrField(body, propertyName);
make no sense and is the source of the exception. In your example, body contains something like p.PersonalDetails.FirstName (String) and the above line is trying to build something like p.PersonalDetails.FirstName.PersonalDetails.FirstName.
Use var leftOperand = body; instead.
You can shorten the whole property path processing by using simple
var leftOperand = propertyName.Split('.')
.Aggregate((Expression)parameterExpression, Expression.PropertyOrField);

How do I merge (or dynamically build and combine) lambda expressions in C#?

I'm trying to dynamically combin lambda expressions. The code below will explain what I want. This is NOT a case of combining a=>b and b=>c to a=>c. Instead, I want to prevent code duplication by reusing conversion-expressions:
class User // convert from...
{
public string FirstName {get;set;}
public string LastName {get;set;}
}
class Person // convert to...
{
public string Name
}
public class UserTransaction
{
User FromUser {get;set;}
User ToUser {get;set;}
decimal Amount {get;set;}
}
public class PersonTransaction
{
Person FromPerson {get;set;}
Person ToPerson {get;set;}
bool IsPositive;
}
Expression<Func<User, Person>> ToPerson = u => new Person {Name = u.FirstName + " " + u.LastName};
Expression<Func<UserTransaction, PersonTransaction>> PersonTransaction = ut => new PersonTransaction {
FromPerson = FromUser.Compile()(ut.FromUser), // Actually, I do not want to compile
ToPerson = ToUser.Compile()(ut.FromUser), // (or double code)
IsPositive = ut.Amount > 0
}
In the example above, I already have an expression to convert a user to a person. I do not want to duplicate this code or compile it. I've tried using stripping out the "compile"-calls by manually editing the expression tree. I did not succeed. Has anybody tried something similar and succeed?
You can do some voodoo with ExpressionVisitor to rewrite your existing code to be fully inline; this:
detects the invoke
locates the Compile
resolves the originating lambda
swaps in all the parameter values directly
rebuilds the expression tree accordingly
Have fun!
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq.Expressions;
using System.Reflection;
public class User // convert from...
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Person // convert to...
{
public string Name { get; set; }
}
public class UserTransaction
{
public User FromUser { get; set; }
public User ToUser { get; set; }
public decimal Amount { get; set; }
}
public class PersonTransaction
{
public Person FromPerson { get; set; }
public Person ToPerson { get; set; }
public bool IsPositive { get; set; }
}
static class Program
{
static void Main()
{
Expression<Func<User, Person>> ToPerson = u => new Person { Name = u.FirstName + " " + u.LastName };
Expression<Func<UserTransaction, PersonTransaction>> PersonTransaction = ut => new PersonTransaction
{
FromPerson = ToPerson.Compile()(ut.FromUser), // Actually, I do not want to compile
ToPerson = ToPerson.Compile()(ut.ToUser), // (or double code)
IsPositive = ut.Amount > 0
};
var visitor = new RemoveCompilationsExpressionVisitor();
var inlined = (Expression<Func<UserTransaction, PersonTransaction>>)visitor.Visit(PersonTransaction);
}
class ParameterSwapExpressionVisitor :ExpressionVisitor
{
private readonly Dictionary<ParameterExpression, Expression> swaps;
public ParameterSwapExpressionVisitor(Dictionary<ParameterExpression, Expression> swaps)
{
this.swaps = swaps;
}
protected override Expression VisitParameter(ParameterExpression node)
{
Expression result;
return swaps.TryGetValue(node, out result) ? result : base.VisitParameter(node);
}
}
class RemoveCompilationsExpressionVisitor : ExpressionVisitor
{
protected override Expression VisitInvocation(InvocationExpression node)
{
var lambda = TryGetInnerLambda(node.Expression);
if(lambda != null)
{
// this would be a partial solution, but we want to go further!
// return Expression.Invoke(lambda, node.Arguments);
var swaps = new Dictionary<ParameterExpression, Expression>();
for(int i = 0; i < lambda.Parameters.Count; i++)
{
swaps.Add(lambda.Parameters[i], node.Arguments[i]);
}
var visitor = new ParameterSwapExpressionVisitor(swaps);
return visitor.Visit(lambda.Body);
}
return base.VisitInvocation(node);
}
LambdaExpression TryGetInnerLambda(Expression node)
{
try
{
if(node.NodeType == ExpressionType.Call)
{
var mce = (MethodCallExpression)node;
var method = mce.Method;
if (method.Name == "Compile" && method.DeclaringType.IsGenericType && method.DeclaringType.GetGenericTypeDefinition()
== typeof(Expression<>))
{
object target;
if (TryGetLiteral(mce.Object, out target))
{
return (LambdaExpression)target;
}
}
}
}
catch (Exception ex)
{
/* best effort only */
Debug.WriteLine(ex);
}
return null;
}
static bool TryGetLiteral(Expression node, out object value)
{
value = null;
if (node == null) return false;
switch(node.NodeType)
{
case ExpressionType.Constant:
value = ((ConstantExpression)node).Value;
return true;
case ExpressionType.MemberAccess:
var me = (MemberExpression)node;
object target;
if (TryGetLiteral(me.Expression, out target))
{
switch (me.Member.MemberType)
{
case System.Reflection.MemberTypes.Field:
value = ((FieldInfo)me.Member).GetValue(target);
return true;
case MemberTypes.Property:
value = ((PropertyInfo)me.Member).GetValue(target, null);
return true;
}
}
break;
}
return false;
}
}
}

Categories

Resources