I would like to know if there is any way to do the next. I would like to extract compiled body of a function.
class Program
{
public static void Main(string[] args)
{
int i = 5;
Where2(p => new { V = p.Length == i });
}
public static void Where2<TResult>(Expression<Func<string, TResult>> func)
{
try
{
var body = (System.Linq.Expressions.NewExpression)func.Body;
Console.WriteLine("Output:");
Console.WriteLine(body);
Console.ReadLine();
}
catch (Exception e)
{
throw e;
}
//dynamic operation = func.Body; //
}
}
Output: new <>f__AnonymousType0`1(V = (p.Length ==
value(TestDelegateFun.Program+<>c__DisplayClass0_0).i))
So I would like to have the compiled body like :
Output: new <>f__AnonymousType0`1(V = (p.Length ==
5))
public class FilterConstructor : ExpressionVisitor
{
private static readonly Dictionary<ExpressionType, string> _logicalOperators;
private static readonly Dictionary<Type, Func<object, string>> _typeConverters;
static FilterConstructor()
{
//mappings for table, shown above
_logicalOperators = new Dictionary<ExpressionType, string>
{
[ExpressionType.Not] = "not",
[ExpressionType.GreaterThan] = "gt",
[ExpressionType.GreaterThanOrEqual] = "ge",
[ExpressionType.LessThan] = "lt",
[ExpressionType.LessThanOrEqual] = "le",
[ExpressionType.Equal] = "==",
[ExpressionType.Not] = "not",
[ExpressionType.AndAlso] = "and",
[ExpressionType.OrElse] = "or"
};
//if type is string we will wrap it into single quotes
//if it is a DateTime we will format it like datetime'2008-07-10T00:00:00Z'
//bool.ToString() returns "True" or "False" with first capital letter, so .ToLower() is applied
//if it is one of the rest "simple" types we will just call .ToString() method on it
_typeConverters = new Dictionary<Type, Func<object, string>>
{
[typeof(string)] = x => $"'{x}'",
[typeof(DateTime)] =
x => $"'{((DateTime)x).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ")}'",
[typeof(bool)] = x => x.ToString().ToLower()
};
}
private StringBuilder _queryStringBuilder;
private Stack<string> _fieldNames;
public FilterConstructor()
{
//here we will collect our query
_queryStringBuilder = new StringBuilder();
//will be discussed below
_fieldNames = new Stack<string>();
}
//entry point
public string GetQuery(LambdaExpression predicate)
{
Visit(predicate.Body);
var query = _queryStringBuilder.ToString();
_queryStringBuilder.Clear();
return query;
}
//protected override Expression VisitUnary(UnaryExpression node)
//{
// //assume we only allow not (!) unary operator:
// if (node.NodeType != ExpressionType.Not)
// throw new NotSupportedException("Only not(\"!\") unary operator is supported!");
// _queryStringBuilder.Append($"{_logicalOperators[node.NodeType]} ");//!
// _queryStringBuilder.Append("("); //(!
// //go down from a tree
// Visit(node.Operand); //(!expression
// _queryStringBuilder.Append(")"); //(!expression)
// //we should return expression, it will allow to create new expression based on existing one,
// //but, at our case, it is not needed, so just return initial node argument
// return node;
//}
//corresponds to: and, or, greater than, less than, etc.
protected override Expression VisitBinary(BinaryExpression node)
{
_queryStringBuilder.Append("("); //(
//left side of binary operator
Visit(node.Left); //(leftExpr
_queryStringBuilder.Append($" {_logicalOperators[node.NodeType]} ");//(leftExpr and
//right side of binary operator
Visit(node.Right); //(leftExpr and RighExpr
_queryStringBuilder.Append(")"); //(leftExpr and RighExpr)
return node;
}
protected override Expression VisitMember(MemberExpression node)
{
if (node.Expression.NodeType == ExpressionType.Constant
||
node.Expression.NodeType == ExpressionType.MemberAccess)
{
_fieldNames.Push(node.Member.Name);
Visit(node.Expression);
}
else
//corresponds to: x.Customer - just write "Customer"
_queryStringBuilder.Append(node.Member.Name);
return node;
}
//corresponds to: 1, "Tom", instance of NameSpace+<>c__DisplayClass12_0, instance of Order, i.e.
//any expression with value
protected override Expression VisitConstant(ConstantExpression node)
{
//just write value
_queryStringBuilder.Append(GetValue(node.Value));
return node;
}
private string GetValue(object input)
{
var type = input.GetType();
//if it is not simple value
if (type.IsClass && type != typeof(string))
{
//proper order of selected names provided by means of Stack structure
var fieldName = _fieldNames.Pop();
var fieldInfo = type.GetField(fieldName);
object value;
if (fieldInfo != null)
//get instance of order
value = fieldInfo.GetValue(input);
else
//get value of "Customer" property on order
value = type.GetProperty(fieldName).GetValue(input);
return GetValue(value);
}
else
{
//our predefined _typeConverters
if (_typeConverters.ContainsKey(type))
return _typeConverters[type](input);
else
//rest types
return input.ToString();
}
}
}
Related
What I am trying to do is probably a little strange.
But I am trying to (this is the best I can explain it) use reflection to get a full class property tree name as a string.
Successful example so far:
By using expressions I am able to convert:
() => Model.Cargo.Id
into the string:
"Model.Cargo.Id"
My problem now is when I am using an array in the mix, I do not get the array name. All I get is the last properties name.
Unsuccessful example:
Model.CargoTasks[j].IsSet
Only returns me the string:
"IsSet"
Ideally I want the following string result:
"Model.CargoTasks[0].IsSet"
I am probably asking a little much to get the index included in the result, but it would be fantasic if this were possible.
The code I am using to process these examples is as follows:
public static string ToMemberAccess<TResult>(this Expression<Func<TResult>> expression)
{
// Get the body of the expression
Expression body = expression.Body;
if (body.NodeType != ExpressionType.MemberAccess && body.NodeType != ExpressionType.Convert)
{
throw new ArgumentException("Property expression must be of the form '() => SomeProperty'", "expression");
}
var memberExpression = expression.Body as MemberExpression ?? ((UnaryExpression)expression.Body).Operand as MemberExpression;
var stuff = GetMemberNames(memberExpression);
stuff.Reverse();
return string.Join(".", stuff);
}
static List<string> GetMemberNames(MemberExpression expression, List<string> actual = null)
{
if (actual == null) actual = new List<string>();
var member = expression.Member;
var subExp = expression.Expression as MemberExpression;
actual.Add(member.Name);
if(subExp != null) actual = GetMemberNames(subExp, actual);
return actual;
}
Thanks in advance! Any help will be greatly appreciated!
To get the value in the indexer, you must compile and execute the expression - which is prohibitively expensive, but it can be done using a modified version of ExpressionStringBuilder. Note that I've added a parameter, compileConstants. When it's set to false, the output will be something like Model.CargoTasks[_.j].IsSet.
Note that this sample visitor is incomplete (i.e. it doesn't support all kinds of expressions). You can complement it using the code in GitHub.
public static string ToMemberAccess<TResult>(Expression<Func<TResult>> expression, bool compileConstants = false)
{
var builder = new ExpressionStringBuilder(compileConstants);
builder.Visit(expression);
return builder.ToString();
}
internal class ExpressionStringBuilder : ExpressionVisitor
{
private readonly bool _compileConstants;
private readonly StringBuilder _out;
public ExpressionStringBuilder(bool compileConstants)
{
_compileConstants = compileConstants;
_out = new StringBuilder();
}
protected override Expression VisitConstant(ConstantExpression node)
{
if (node.Value != null)
{
string text = node.Value.ToString();
if (node.Value is string)
{
Out("\"");
Out(text);
Out("\"");
}
else if (text == node.Value.GetType().ToString())
{
Out('_');
}
else
{
Out(text);
}
}
else
{
Out("null");
}
return node;
}
protected override Expression VisitMethodCall(MethodCallExpression node)
{
int num = 0;
Expression expression = node.Object;
if (Attribute.GetCustomAttribute(node.Method, typeof(ExtensionAttribute)) != null)
{
num = 1;
expression = node.Arguments[0];
}
var name = node.Method.Name;
var isIndexer = name == "get_Item";
if (expression != null)
{
Visit(expression);
if (!isIndexer)
{
Out('.');
}
}
if (isIndexer)
Out('[');
else
{
Out(name);
Out('(');
}
int i = num;
int count = node.Arguments.Count;
while (i < count)
{
if (i > num)
{
Out(", ");
}
VisitArgument(node.Arguments[i]);
i++;
}
Out(isIndexer ? ']' : ')');
return node;
}
protected override Expression VisitIndex(IndexExpression node)
{
if (node.Object != null)
{
Visit(node.Object);
}
else
{
Out(node.Indexer.DeclaringType.Name);
}
if (node.Indexer != null)
{
Out(".");
Out(node.Indexer.Name);
}
Out('[');
for (var index = 0; index < node.Arguments.Count; index++)
{
if (index > 0)
{
Out(", ");
}
var expression = node.Arguments[index];
VisitArgument(expression);
}
Out(']');
return node;
}
protected override Expression VisitLambda<T>(Expression<T> node)
{
Visit(node.Body);
return node;
}
protected override Expression VisitMember(MemberExpression node)
{
OutMember(node.Expression, node.Member);
return node;
}
public override string ToString()
{
return _out.ToString();
}
private void VisitArgument(Expression expression)
{
if (_compileConstants)
{
// TODO: possibly check the expression is not dependent on parameters
var value = Expression.Lambda(expression).Compile().DynamicInvoke();
Out(value + string.Empty);
}
else
{
VisitArgument(expression);
}
}
private void OutMember(Expression instance, MemberInfo member)
{
if (instance != null)
{
Visit(instance);
if (_out.Length > 0)
Out('.');
Out(member.Name);
return;
}
Out(member.DeclaringType.Name + "." + member.Name);
}
private void Out(char c)
{
_out.Append(c);
}
private void Out(string s)
{
_out.Append(s);
}
}
I have an expression like: Expression<Func<TheObject, int, bool>> myExpression = (myObj, theType) => { myObj.Prop > theType };
I need to dynamically rebuild myExpression to a new expression of type Expression<Func<TheObject, bool>> and replace “theType” parameter from the first expression with a concrete value 123 like:
Expression<Func<TheObject, bool>> myNewExpression = myObj => { myObj.Prop > 123 };
How can I do that?
Br
Philip
With a simple expression replacer it is quite easy:
This is the one I've written for myself... supports simple replaces and multiple replaces.
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
// A simple expression visitor to replace some nodes of an expression
// with some other nodes. Can be used with anything, not only with
// ParameterExpression
public class SimpleExpressionReplacer : ExpressionVisitor
{
public readonly Dictionary<Expression, Expression> Replaces;
public SimpleExpressionReplacer(Expression from, Expression to)
{
Replaces = new Dictionary<Expression, Expression> { { from, to } };
}
public SimpleExpressionReplacer(Dictionary<Expression, Expression> replaces)
{
// Note that we should really clone from and to... But we will
// ignore this!
Replaces = replaces;
}
public SimpleExpressionReplacer(IEnumerable<Expression> from, IEnumerable<Expression> to)
{
Replaces = new Dictionary<Expression, Expression>();
using (var enu1 = from.GetEnumerator())
using (var enu2 = to.GetEnumerator())
{
while (true)
{
bool res1 = enu1.MoveNext();
bool res2 = enu2.MoveNext();
if (!res1 || !res2)
{
if (!res1 && !res2)
{
break;
}
if (!res1)
{
throw new ArgumentException("from shorter");
}
throw new ArgumentException("to shorter");
}
Replaces.Add(enu1.Current, enu2.Current);
}
}
}
public override Expression Visit(Expression node)
{
Expression to;
if (node != null && Replaces.TryGetValue(node, out to))
{
return base.Visit(to);
}
return base.Visit(node);
}
}
Your TheObject
public class TheObject
{
public int Prop { get; set; }
}
Then you only need to replace the 2nd parameter in the body of the expression and rebuild the Expression<>
public class Program
{
public static void Main(string[] args)
{
Expression<Func<TheObject, int, bool>> myExpression = (myObj, theType) => myObj.Prop > theType;
int value = 123;
var body = myExpression.Body;
var body2 = new SimpleExpressionReplacer(myExpression.Parameters[1], Expression.Constant(value)).Visit(body);
Expression<Func<TheObject, bool>> myExpression2 = Expression.Lambda<Func<TheObject, bool>>(body2, myExpression.Parameters[0]);
}
}
I have the following LINQ query:
var result = from person in dbContext.Person
select new
{
FirstName = person.FirstName,
LastName = person.LastName,
// I want to save this logic
JobCount = person.Jobs.Count(x => x.Completed)
};
}
To avoid repeating myself in other LINQ queries, I would like to make the JobCount lambda logic available for use in other queries.
I thought I might be able to use Func<Person, int>, like so:
public Func<Person, int> GetCompletedJobsForPerson = person => person.Jobs.Count(x => x.Completed);
var result = from person in dbContext.Person
select new
{
FirstName = person.FirstName,
LastName = person.LastName,
// Use Invoke to get amount
JobCount = GetCompletedJobsForPerson.Invoke(person)
};
}
PROBLEM STATEMENT: This fails because the method cannot be mapped to an SQL statement and causes a NotSupportedException
NotSupportedException was unhandled
The LINQ expression node type 'Invoke' is not supported in LINQ to Entities.
How can I make the lambda reusable from multiple LINQ queries?
It can't be done in an easy way (and if it could be done easily, someone would have done it :-) )
What could be done is use the same trick of PredicateBuilder and create an AsExpandable that will replace some "tokens" (function calls) in your query with some other function calls. But I don't think it's worth the trouble. It is some hundred lines of code to do it "correctly".
The other problem is that the query would then need this special method to be called:
var result = (from person in dbContext.Person
select new
{
FirstName = person.FirstName,
LastName = person.LastName,
// Use Invoke to get amount
JobCount = GetCompletedJobsForPerson(person)
}).FixMethodCalls();
Ok... it was difficult, but doable:
// v0.11 Codename: Handle with Care+ (+ == Plus)
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property)]
public class ExpandableAttribute : Attribute
{
// Just to know the suffix to use :-)
public static readonly string ExpandableSuffix = "Expression";
}
// Replaces method and properties calls to "special" method calls that
// are Expression(s). These method/property calls can be used anywhere in
// the query (Select, Where, GroupBy, ...)
// Remember to use .AsExpandable2() somewhere in your query (it must be a
// "top level" part of the query):
// OK:
// var res1 = (from x in table select x).AsExpandable2();
// var res2 = table.AsExpandable().Where(x => true);
// var res3 = table.Where(x => true).AsExpandable2();
// var res4 = table.Where(x => true).AsExpandable2().Select(x => x);
// var res5 = table.Where(x => true).AsExpandable2().Select(x => x).AsExpandable2();
// Not OK:
// var res1 = table.Select(x => x.subtable.AsExpandable2());
// **Method calls**
// The methods to be expanded can be static or instance. There must be
// a corresponding **static* method with same name and suffix
// "Expression", that doesn't have parameters and returns an Expression
// with a certain signature.
// Static:
// var res2 = table.AsExpandable2().Select(x => MyClass.StaticMethod(1, x, 2, 3));
// There must be in the class MyClass
// public/private/protected static Expression<Func<int, MyClass, int, int, returnType(StaticMethod)>> StaticMethodExpression()
// Instance:
// var res1 = table.AsExpandable2().Select(x => x.InstanceMethod(1, 2, 3));
// There must be in the class x.GetType()
// public/private/protected static Expression<Func<x.GetType(), int, int, int, returnType(InstanceMethod)>> InstanceMethodExpression()
// Note that multiple "tables" can be passed as parameters:
// Static:
// var res3 = (from x in table1 from y in table2 select new { x, y }).AsExpandable2().Select(z => MyClass.StaticMethod(1, z.x, z.y, 2, 3));
// There must be in the class MyClass
// public/private/protected/internal static Expression<Func<int, x.GetType(), y.GetType(), int, int, returnType(StaticMethod)>> StaticMethodExpression()
// Instance:
// var res4 = (from x in table1 from y in table2 select new { x, y }).AsExpandable2().Select(z => z.x.StaticMethod(1, z.y, 2, 3));
// There must be in the class x.GetType()
// public/private/protected/internal static Expression<Func<x.GetType(), int, y.GetType(), int, int, returnType(StaticMethod)>> InstanceMethodExpression()
// **Properties**
// Same as with method calls, but with properties :-)
// (useful for things like FullName, where
// FullName = Name + ' ' + Surname)
// Remember that the *Expression property must be **static**!
// Static (not very useful :-) ):
// var res1 = table.AsExpandable2().Select(x => MyClass.StaticProperty);
// There must be in the class MyClass
// public/private/protected/internal static Expression<Func<MyClass.StaticProperty.GetType()>> StaticPropertyExpression { get; }
// Instance:
// var res2 = table.AsExpandable2().Select(x => x.InstanceProperty);
// There must be in the class x.GetType()
// public/private/protected/internal static Expression<Func<x.GetType(), x.InstanceProperty.GetType())>> InstancePropertyExpression { get; }
public static class MethodsPropertiesExpander
{
// Because AsExpandable() is already used by http://www.albahari.com/nutshell/linqkit.aspx
public static IQueryable<T> AsExpandable2<T>(this IQueryable<T> source)
{
if (source is MethodsPropertiesExpander<T>)
{
return source;
}
return new MethodsPropertiesExpander<T>(source);
}
}
public interface IMethodsPropertiesExpander
{
}
public class MethodsPropertiesExpander<T> : IOrderedQueryable<T>, IQueryProvider, IMethodsPropertiesExpander
{
public readonly IQueryable<T> Query;
public MethodsPropertiesExpander(IQueryable<T> query)
{
if (!(query is IMethodsPropertiesExpander))
{
Expression expression = MethodsPropertiesReplacer.Default.Visit(query.Expression);
Query = expression == query.Expression ? query : query.Provider.CreateQuery<T>(expression);
}
else
{
Query = query;
}
}
/* IQueryable<T> */
public IEnumerator<T> GetEnumerator()
{
return Query.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public Type ElementType
{
get { return Query.ElementType; }
}
public Expression Expression
{
get { return Query.Expression; }
}
public IQueryProvider Provider
{
get { return this; }
}
/* IQueryProvider */
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return new MethodsPropertiesExpander<TElement>(Query.Provider.CreateQuery<TElement>(expression));
}
public IQueryable CreateQuery(Expression expression)
{
Type iqueryableArgument = GetIQueryableTypeArgument(expression.Type);
MethodInfo createQueryImplMethod = typeof(MethodsPropertiesExpander<T>)
.GetMethod("CreateQuery", BindingFlags.Instance | BindingFlags.NonPublic)
.MakeGenericMethod(iqueryableArgument);
return (IQueryable)createQueryImplMethod.Invoke(this, new[] { expression });
}
public TResult Execute<TResult>(Expression expression)
{
if (!(Query.Provider is IMethodsPropertiesExpander))
{
// We want to expand it only once :-)
expression = MethodsPropertiesReplacer.Default.Visit(expression);
}
return Query.Provider.Execute<TResult>(expression);
}
public object Execute(Expression expression)
{
if (!(Query.Provider is IMethodsPropertiesExpander))
{
// We want to expand it only once :-)
expression = MethodsPropertiesReplacer.Default.Visit(expression);
}
return Query.Provider.Execute(expression);
}
/* Implementation methods */
/// <summary>
/// Gets the T of IQueryablelt;T>
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
protected static Type GetIQueryableTypeArgument(Type type)
{
IEnumerable<Type> interfaces = type.IsInterface ?
new[] { type }.Concat(type.GetInterfaces()) :
type.GetInterfaces();
Type argument = (from x in interfaces
where x.IsGenericType
let gt = x.GetGenericTypeDefinition()
where gt == typeof(IQueryable<>)
select x.GetGenericArguments()[0]).FirstOrDefault();
return argument;
}
/* Utility classes */
protected sealed class MethodsPropertiesReplacer : ExpressionVisitor
{
// Single instance is enough!
public static readonly MethodsPropertiesReplacer Default = new MethodsPropertiesReplacer();
private MethodsPropertiesReplacer()
{
}
protected override Expression VisitMember(MemberExpression node)
{
PropertyInfo property = node.Member as PropertyInfo;
MethodInfo getter;
// We handle only properties (that aren't indexers) that have
// a get
if (property != null && property.GetIndexParameters().Length == 0 && (getter = property.GetGetMethod(true)) != null)
{
// We work only on methods marked as [ExpandableAttribute]
var attribute = property.GetCustomAttributes(typeof(ExpandableAttribute), false).FirstOrDefault();
if (attribute != null)
{
string name = property.Name + ExpandableAttribute.ExpandableSuffix;
var property2 = property.DeclaringType.GetProperty(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, null, Type.EmptyTypes, null);
if (property2 == null || property2.GetGetMethod(true) == null)
{
if (property2 == null)
{
if (property.DeclaringType.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, null, Type.EmptyTypes, null) != null)
{
throw new NotSupportedException(string.Format("{0}.{1} isn't static!", property.DeclaringType.FullName, name));
}
throw new NotSupportedException(string.Format("{0}.{1} not found!", property.DeclaringType.FullName, name));
}
// property2.GetGetMethod(true) == null
throw new NotSupportedException(string.Format("{0}.{1} doesn't have a getter!", property.DeclaringType.FullName, name));
}
// Instance Parameters have the additional
// "parameter" of the declaring type
var argumentsPlusReturnTypes = getter.IsStatic ?
new[] { node.Type } :
new[] { property.DeclaringType, node.Type };
var funcType = typeof(Func<>).Assembly.GetType(string.Format("System.Func`{0}", argumentsPlusReturnTypes.Length));
var returnType = typeof(Expression<>).MakeGenericType(funcType.MakeGenericType(argumentsPlusReturnTypes));
if (property2.PropertyType != returnType)
{
throw new NotSupportedException(string.Format("{0}.{1} has wrong return type!", property.DeclaringType.FullName, name));
}
var expression = (LambdaExpression)property2.GetValue(null, null);
// Instance Members have the additional "parameter"
// of the declaring type
var arguments2 = getter.IsStatic ? new Expression[0] : new[] { node.Expression };
var replacer = new SimpleExpressionReplacer(expression.Parameters, arguments2);
var body = replacer.Visit(expression.Body);
return this.Visit(body);
}
}
return base.VisitMember(node);
}
protected override Expression VisitMethodCall(MethodCallExpression node)
{
MethodInfo method = node.Method;
// We work only on methods marked as [ExpandableAttribute]
var attribute = method.GetCustomAttributes(typeof(ExpandableAttribute), false).FirstOrDefault();
if (attribute != null)
{
string name = method.Name + ExpandableAttribute.ExpandableSuffix;
var method2 = method.DeclaringType.GetMethod(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
if (method2 == null)
{
if (method.DeclaringType.GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null) != null)
{
throw new NotSupportedException(string.Format("{0}.{1} isn't static!", method.DeclaringType.FullName, name));
}
throw new NotSupportedException(string.Format("{0}.{1} not found!", method.DeclaringType.FullName, name));
}
// Instance methods have the additional "parameter" of
// the declaring type
var argumentsPlusReturnTypes = method.IsStatic ?
node.Arguments.Select(x => x.Type).Concat(new[] { node.Type }).ToArray() :
new[] { method.DeclaringType }.Concat(node.Arguments.Select(x => x.Type)).Concat(new[] { node.Type }).ToArray();
var funcType = typeof(Func<>).Assembly.GetType(string.Format("System.Func`{0}", argumentsPlusReturnTypes.Length));
var returnType = typeof(Expression<>).MakeGenericType(funcType.MakeGenericType(argumentsPlusReturnTypes));
if (method2.ReturnType != returnType)
{
throw new NotSupportedException(string.Format("{0}.{1} has wrong return type!", method.DeclaringType.FullName, name));
}
var expression = (LambdaExpression)method2.Invoke(null, null);
// Instance methods have the additional "parameter" of
// the declaring type
var arguments2 = method.IsStatic ? node.Arguments : new[] { node.Object }.Concat(node.Arguments);
var replacer = new SimpleExpressionReplacer(expression.Parameters, arguments2);
var body = replacer.Visit(expression.Body);
return this.Visit(body);
}
return base.VisitMethodCall(node);
}
}
}
// A simple expression visitor to replace some nodes of an expression
// with some other nodes
public class SimpleExpressionReplacer : ExpressionVisitor
{
public readonly Dictionary<Expression, Expression> Replaces;
public SimpleExpressionReplacer(Dictionary<Expression, Expression> replaces)
{
Replaces = replaces;
}
public SimpleExpressionReplacer(IEnumerable<Expression> from, IEnumerable<Expression> to)
{
Replaces = new Dictionary<Expression, Expression>();
using (var enu1 = from.GetEnumerator())
using (var enu2 = to.GetEnumerator())
{
while (true)
{
bool res1 = enu1.MoveNext();
bool res2 = enu2.MoveNext();
if (!res1 || !res2)
{
if (!res1 && !res2)
{
break;
}
if (!res1)
{
throw new ArgumentException("from shorter");
}
throw new ArgumentException("to shorter");
}
Replaces.Add(enu1.Current, enu2.Current);
}
}
}
public SimpleExpressionReplacer(Expression from, Expression to)
{
Replaces = new Dictionary<Expression, Expression> { { from, to } };
}
public override Expression Visit(Expression node)
{
Expression to;
if (node != null && Replaces.TryGetValue(node, out to))
{
return base.Visit(to);
}
return base.Visit(node);
}
}
I've added a bonus: you can even "expand" special properties. The instructions on how to use it are in the big comment at the beginning. Now I'll give you some examples:
// Generated by EF
public partial class MyClass
{
public int ID { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public ICollection<MyInnerClass> MyInnerClass;
}
// Written by you (remember the partial!)
public partial class MyClass
{
[Expandable]
public int CountMyInnerClass()
{
// Not necessary to implement, unless you want to use it C#-side
throw new NotImplementedException();
}
[Expandable]
public int CountMyInnerClassPlus(int num)
{
// Not necessary to implement, unless you want to use it C#-side
throw new NotImplementedException();
}
[Expandable]
public int CountMyInnerClassProperty
{
get
{
// Not necessary to implement, unless you want to use it C#-side
throw new NotImplementedException();
}
}
[Expandable]
public string FullName
{
get
{
// Not necessary to implement, unless you want to use it C#-side
return Name + " " + Surname;
}
}
protected static Expression<Func<MyClass, int>> CountMyInnerClassExpression()
{
return x => x.MyInnerClass.Count();
}
protected static Expression<Func<MyClass, int, int>> CountMyInnerClassPlusExpression()
{
return (x, num) => x.MyInnerClass.Count() + num;
}
protected static Expression<Func<MyClass, int>> CountMyInnerClassPropertyExpression
{
get
{
return x => x.MyInnerClass.Count();
}
}
protected static Expression<Func<MyClass, string>> FullNameExpression
{
get
{
return x => x.Name + " " + x.Surname;
}
}
}
and then, in some other class class (perhaps the ones of the query):
[Expandable]
public static int LocalCountMyInnerClassPlus(MyClass x, int num)
{
// Not necessary to implement, unless you want to use it C#-side
throw new NotImplementedException();
}
public static Expression<Func<MyClass, int, int>> LocalCountMyInnerClassPlusExpression()
{
return (x, num) => x.MyInnerClass.Count() + num;
}
and then
var query = (from x in db.MyClasses
select new
{
x.ID,
x.FullName,
Count1 = x.CountMyInnerClass(),
Count2 = x.CountMyInnerClassPlus(5),
Count3 = x.CountMyInnerClassProperty,
Count4 = LocalCountMyInnerClassPlus(x, 10),
}).AsExpandable2().ToList();
and it just works :-)
I am trying to copy the behavior of Entity Framework in creating the query from expression and i found my way using ExpressionVisitor when getting the property of the model by using Attribute
this is what i got so far
internal class NVisitor : ExpressionVisitor
{
private readonly ParameterExpression _parameter;
private readonly Type _type;
public NVisitor(Type type)
{
_type = type;
_parameter = Expression.Parameter(type);
}
protected override Expression VisitParameter(ParameterExpression node)
{
return _parameter;
}
protected override Expression VisitMember(MemberExpression node)
{
if (node.Member.MemberType == MemberTypes.Property)
{
var memberName = node.Member.Name;
PropertyInfo otherMember = _type.GetProperty(memberName);
var ncols = node.Member.GetCustomAttributes(typeof(NColumn), true);
if (ncols.Any())
{
var ncol = (NColumn)ncols.First();
otherMember = _type.GetProperty(ncol.Name);
}
var inner = Visit(node.Expression);
return Expression.Property(inner, otherMember);
}
return base.VisitMember(node);
}
}
i have an attribute NColumn that indicates the real name of the property from table column so i mark the property of the model by Attribute
public class BonusTypeX
{
[NColumn("BonusTypeName")]
public string Name { get; set; }
}
now when im trying to get the expression,
[TestMethod]
public void ExpressionTesting2()
{
string searchKey = "Xmas";
Expression<Func<BonusTypeX, bool>> expression = x => x.Name.Contains(searchKey);
Type t = typeof(tbl_BonusType);
var body = new NVisitor(t).Visit(expression.Body);
string a = string.Join(".", body.ToString().Split('.').Skip(1));
Assert.AreEqual("BonusTypeName.Contains(\"Xmas\")", a);
}
i got this
BonusTypeName.Contains(value(Payroll.Test.Administration.TestRepositories+<>c__DisplayClass13).searchKey)
what i am expecting to get is
BonusTypeName.Contains("Xmas")
is there any method that gets the expression string? i am using
string a = string.Join(".", body.ToString().Split('.').Skip(1));
which i think it might be wrong.. :)
any help would be appreciated.
Local variable are captured in a compiler generated class at runtime, this explains the Payroll.Test.Administration.TestRepositories+<>c__DisplayClass13).searchKey part. To get the generated field's value in your expression you must explicitly replace it's value when visiting the expression:
protected override Expression VisitMember(MemberExpression node)
{
if (node.Member.MemberType == MemberTypes.Property)
{
var memberName = node.Member.Name;
PropertyInfo otherMember = _type.GetProperty(memberName);
var ncols = node.Member.GetCustomAttributes(typeof(NColumn), true);
if (ncols.Any())
{
var ncol = (NColumn)ncols.First();
otherMember = _type.GetProperty(ncol.Name);
}
var inner = Visit(node.Expression);
return Expression.Property(inner, otherMember);
}
if (node.Member.MemberType == MemberTypes.Field)
{
if (node.Expression is ConstantExpression)
{
var owner = ((ConstantExpression)node.Expression).Value;
var value = Expression.Constant(((FieldInfo)node.Member).GetValue(owner));
return value;
}
}
return base.VisitMember(node);
}
Intro
In the application I 'm currently working on, there are two kinds of each business object: the "ActiveRecord" kind and the "DataContract" kind. So for example, there would be:
namespace ActiveRecord {
class Widget {
public int Id { get; set; }
}
}
namespace DataContract {
class Widget {
public int Id { get; set; }
}
}
The database access layer takes care of translating between families: you can tell it to update a DataContract.Widget and it will magically create an ActiveRecord.Widget with the same property values and save that instead.
The problem surfaced when attempting to refactor this database access layer.
The Problem
I want to add methods like the following to the database access layer:
// Widget is DataContract.Widget
interface IDbAccessLayer {
IEnumerable<Widget> GetMany(Expression<Func<Widget, bool>> predicate);
}
The above is a simple general-use "get" method with custom predicate. The only point of interest is that I am passing in an expression tree instead of a lambda because inside IDbAccessLayer I am querying an IQueryable<ActiveRecord.Widget>; to do that efficiently (think LINQ to SQL) I need to pass in an expression tree so this method asks for just that.
The snag: the parameter needs to be magically transformed from an Expression<Func<DataContract.Widget, bool>> to an Expression<Func<ActiveRecord.Widget, bool>>.
Attempted Solution
What I 'd like to do inside GetMany is:
IEnumerable<DataContract.Widget> GetMany(
Expression<Func<DataContract.Widget, bool>> predicate)
{
var lambda = Expression.Lambda<Func<ActiveRecord.Widget, bool>>(
predicate.Body,
predicate.Parameters);
// use lambda to query ActiveRecord.Widget and return some value
}
This won't work because in a typical scenario, for example if:
predicate == w => w.Id == 0;
...the expression tree contains a MemberAccessExpression instance which has a property of type MemberInfo that describes DataContract.Widget.Id.
There are also ParameterExpression instances both in the expression tree and in its parameter collection (predicate.Parameters) that describe DataContract.Widget; all of this will result in errors since the queryable body does not contain that type of widget but rather ActiveRecord.Widget.
After searching a bit, I found System.Linq.Expressions.ExpressionVisitor (its source can be found here in the context of a how-to), which offers a convenient way to modify an expression tree. In .NET 4, this class is included out of the box.
Armed with this, I implemented a visitor. This simple visitor only takes care of changing the types in member access and parameter expressions, but that's enough functionality to work with the predicate w => w.Id == 0.
internal class Visitor : ExpressionVisitor
{
private readonly Func<Type, Type> typeConverter;
public Visitor(Func<Type, Type> typeConverter)
{
this.typeConverter = typeConverter;
}
protected override Expression VisitMember(MemberExpression node)
{
var dataContractType = node.Member.ReflectedType;
var activeRecordType = this.typeConverter(dataContractType);
var converted = Expression.MakeMemberAccess(
base.Visit(node.Expression),
activeRecordType.GetProperty(node.Member.Name));
return converted;
}
protected override Expression VisitParameter(ParameterExpression node)
{
var dataContractType = node.Type;
var activeRecordType = this.typeConverter(dataContractType);
return Expression.Parameter(activeRecordType, node.Name);
}
}
With this visitor, GetMany becomes:
IEnumerable<DataContract.Widget> GetMany(
Expression<Func<DataContract.Widget, bool>> predicate)
{
var visitor = new Visitor(...);
var lambda = Expression.Lambda<Func<ActiveRecord.Widget, bool>>(
visitor.Visit(predicate.Body),
predicate.Parameters.Select(p => visitor.Visit(p));
var widgets = ActiveRecord.Widget.Repository().Where(lambda);
// This is just for reference, see below
Expression<Func<ActiveRecord.Widget, bool>> referenceLambda =
w => w.Id == 0;
// Here we 'd convert the widgets to instances of DataContract.Widget and
// return them -- this has nothing to do with the question though.
}
Results
The good news is that lambda is constructed just fine. The bad news is that it isn't working; it's blowing up on me when I try to use it, and the exception messages are really not helpful at all.
I have examined the lambda my code produces and a hardcoded lambda with the same expression; they look exactly the same. I spent hours in the debugger trying to find some difference, but I can't.
When the predicate is w => w.Id == 0, lambda looks exactly like referenceLambda. But the latter works with e.g. IQueryable<T>.Where, while the former does not; I have tried this in the immediate window of the debugger.
I should also mention that when the predicate is w => true, everything works fine. Therefore I am assuming that I 'm not doing enough work in the visitor, but I can't find any more leads to follow.
Final Solution
After taking into account the correct answers to the problem (two of them below; one short, one with code) the problem was solved; I put the code along with a few important notes in a separate answer to keep this long question from becoming even longer.
Thanks to everyone for your answers and comments!
It seems you're generating the parameter expression twice, in VisitMember() here:
var converted = Expression.MakeMemberAccess(
base.Visit(node.Expression),
activeRecordType.GetProperty(node.Member.Name));
...since base.Visit() will end up in VisitParameter I imagine, and in GetMany() itself:
var lambda = Expression.Lambda<Func<ActiveRecord.Widget, bool>>(
visitor.Visit(predicate.Body),
predicate.Parameters.Select(p => visitor.Visit(p));
If you're using a ParameterExpression in the body, it has to be the same instance (not just the same type and name) as the one declared for the Lambda.
I've had problems before with this kind of scenario, though I think the result was that I just wasn't able to create the expression, it would just throw an exception. In any case you might try reusing the parameter instance see if it helps.
It turned out that the tricky part is simply that the ParameterExpression instances that exist in the expression tree of the new lambda must be the same instances as are passed in the IEnumerable<ParameterExpression> parameter of Expression.Lambda.
Note that inside TransformPredicateLambda I am giving t => typeof(TNewTarget) as the "type converter" function; that's because in this specific case, we can assume that all parameters and member accesses will be of that one specific type. More advanced scenarios may need additional logic in there.
The code:
internal class DbAccessLayer {
private static Expression<Func<TNewTarget, bool>>
TransformPredicateLambda<TOldTarget, TNewTarget>(
Expression<Func<TOldTarget, bool>> predicate)
{
var lambda = (LambdaExpression) predicate;
if (lambda == null) {
throw new NotSupportedException();
}
var mutator = new ExpressionTargetTypeMutator(t => typeof(TNewTarget));
var explorer = new ExpressionTreeExplorer();
var converted = mutator.Visit(predicate.Body);
return Expression.Lambda<Func<TNewTarget, bool>>(
converted,
lambda.Name,
lambda.TailCall,
explorer.Explore(converted).OfType<ParameterExpression>());
}
private class ExpressionTargetTypeMutator : ExpressionVisitor
{
private readonly Func<Type, Type> typeConverter;
public ExpressionTargetTypeMutator(Func<Type, Type> typeConverter)
{
this.typeConverter = typeConverter;
}
protected override Expression VisitMember(MemberExpression node)
{
var dataContractType = node.Member.ReflectedType;
var activeRecordType = this.typeConverter(dataContractType);
var converted = Expression.MakeMemberAccess(
base.Visit(node.Expression),
activeRecordType.GetProperty(node.Member.Name));
return converted;
}
protected override Expression VisitParameter(ParameterExpression node)
{
var dataContractType = node.Type;
var activeRecordType = this.typeConverter(dataContractType);
return Expression.Parameter(activeRecordType, node.Name);
}
}
}
/// <summary>
/// Utility class for the traversal of expression trees.
/// </summary>
public class ExpressionTreeExplorer
{
private readonly Visitor visitor = new Visitor();
/// <summary>
/// Returns the enumerable collection of expressions that comprise
/// the expression tree rooted at the specified node.
/// </summary>
/// <param name="node">The node.</param>
/// <returns>
/// The enumerable collection of expressions that comprise the expression tree.
/// </returns>
public IEnumerable<Expression> Explore(Expression node)
{
return this.visitor.Explore(node);
}
private class Visitor : ExpressionVisitor
{
private readonly List<Expression> expressions = new List<Expression>();
protected override Expression VisitBinary(BinaryExpression node)
{
this.expressions.Add(node);
return base.VisitBinary(node);
}
protected override Expression VisitBlock(BlockExpression node)
{
this.expressions.Add(node);
return base.VisitBlock(node);
}
protected override Expression VisitConditional(ConditionalExpression node)
{
this.expressions.Add(node);
return base.VisitConditional(node);
}
protected override Expression VisitConstant(ConstantExpression node)
{
this.expressions.Add(node);
return base.VisitConstant(node);
}
protected override Expression VisitDebugInfo(DebugInfoExpression node)
{
this.expressions.Add(node);
return base.VisitDebugInfo(node);
}
protected override Expression VisitDefault(DefaultExpression node)
{
this.expressions.Add(node);
return base.VisitDefault(node);
}
protected override Expression VisitDynamic(DynamicExpression node)
{
this.expressions.Add(node);
return base.VisitDynamic(node);
}
protected override Expression VisitExtension(Expression node)
{
this.expressions.Add(node);
return base.VisitExtension(node);
}
protected override Expression VisitGoto(GotoExpression node)
{
this.expressions.Add(node);
return base.VisitGoto(node);
}
protected override Expression VisitIndex(IndexExpression node)
{
this.expressions.Add(node);
return base.VisitIndex(node);
}
protected override Expression VisitInvocation(InvocationExpression node)
{
this.expressions.Add(node);
return base.VisitInvocation(node);
}
protected override Expression VisitLabel(LabelExpression node)
{
this.expressions.Add(node);
return base.VisitLabel(node);
}
protected override Expression VisitLambda<T>(Expression<T> node)
{
this.expressions.Add(node);
return base.VisitLambda(node);
}
protected override Expression VisitListInit(ListInitExpression node)
{
this.expressions.Add(node);
return base.VisitListInit(node);
}
protected override Expression VisitLoop(LoopExpression node)
{
this.expressions.Add(node);
return base.VisitLoop(node);
}
protected override Expression VisitMember(MemberExpression node)
{
this.expressions.Add(node);
return base.VisitMember(node);
}
protected override Expression VisitMemberInit(MemberInitExpression node)
{
this.expressions.Add(node);
return base.VisitMemberInit(node);
}
protected override Expression VisitMethodCall(MethodCallExpression node)
{
this.expressions.Add(node);
return base.VisitMethodCall(node);
}
protected override Expression VisitNew(NewExpression node)
{
this.expressions.Add(node);
return base.VisitNew(node);
}
protected override Expression VisitNewArray(NewArrayExpression node)
{
this.expressions.Add(node);
return base.VisitNewArray(node);
}
protected override Expression VisitParameter(ParameterExpression node)
{
this.expressions.Add(node);
return base.VisitParameter(node);
}
protected override Expression VisitRuntimeVariables(RuntimeVariablesExpression node)
{
this.expressions.Add(node);
return base.VisitRuntimeVariables(node);
}
protected override Expression VisitSwitch(SwitchExpression node)
{
this.expressions.Add(node);
return base.VisitSwitch(node);
}
protected override Expression VisitTry(TryExpression node)
{
this.expressions.Add(node);
return base.VisitTry(node);
}
protected override Expression VisitTypeBinary(TypeBinaryExpression node)
{
this.expressions.Add(node);
return base.VisitTypeBinary(node);
}
protected override Expression VisitUnary(UnaryExpression node)
{
this.expressions.Add(node);
return base.VisitUnary(node);
}
public IEnumerable<Expression> Explore(Expression node)
{
this.expressions.Clear();
this.Visit(node);
return expressions.ToArray();
}
}
}
I tried the simple (not complete) implementation for mutating the expression p => p.Id == 15 (the code is below). There are one class named "CrossMapping" which defines the mapping between original and "new" types and type members.
There are several metods named Mutate_XY_Expression for every expression type, which makes new mutated expression. The method inputs need the original express (MemberExpression originalExpression) as model of expression, the list or parameters expression (IList<ParameterExpression> parameterExpressions) which are defined parameters by "parent" expression and should be used by "parent's" body, and the mapping object (CrossMapping mapping) which defines the mapping between types and members.
For full implementation you will maybe need more informations from parent's expression than parameters. But the pattern should be same.
Sample does not implement the Visitor pattern, as you know - it's because simplicity. But there is no barrier to converting to them.
I hope, it will help.
The code (C# 4.0):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Linq.Expressions;
namespace ConsoleApplication1 {
public class Product1 {
public int Id { get; set; }
public string Name { get; set; }
public decimal Weight { get; set; }
}
public class Product2 {
public int Id { get; set; }
public string Name { get; set; }
public decimal Weight { get; set; }
}
class Program {
static void Main( string[] args ) {
// list of products typed as Product1
var lst1 = new List<Product1> {
new Product1{ Id = 1, Name = "One" },
new Product1{ Id = 15, Name = "Fifteen" },
new Product1{ Id = 9, Name = "Nine" }
};
// the expression for filtering products
// typed as Product1
Expression<Func<Product1, bool>> q1;
q1 = p => p.Id == 15;
// list of products typed as Product2
var lst2 = new List<Product2> {
new Product2{ Id = 1, Name = "One" },
new Product2{ Id = 15, Name = "Fifteen" },
new Product2{ Id = 9, Name = "Nine" }
};
// type of Product1
var tp1 = typeof( Product1 );
// property info of "Id" property from type Product1
var tp1Id = tp1.GetProperty( "Id", BindingFlags.Public | BindingFlags.Instance );
// delegate type for predicating for Product1
var tp1FuncBool = typeof( Func<,> ).MakeGenericType( tp1, typeof( bool ) );
// type of Product2
var tp2 = typeof( Product2 );
// property info of "Id" property from type Product2
var tp21Id = tp2.GetProperty( "Id", BindingFlags.Public | BindingFlags.Instance );
// delegate type for predicating for Product2
var tp2FuncBool = typeof( Func<,> ).MakeGenericType( tp2, typeof( bool ) );
// mapping object for types and type members
var cm1 = new CrossMapping {
TypeMapping = {
// Product1 -> Product2
{ tp1, tp2 },
// Func<Product1, bool> -> Func<Product2, bool>
{ tp1FuncBool, tp2FuncBool }
},
MemberMapping = {
// Product1.Id -> Product2.Id
{ tp1Id, tp21Id }
}
};
// mutate express from Product1's "enviroment" to Product2's "enviroment"
var cq1_2 = MutateExpression( q1, cm1 );
// compile lambda to delegate
var dlg1_2 = ((LambdaExpression)cq1_2).Compile();
// executing delegate
var rslt1_2 = lst2.Where( (Func<Product2, bool>)dlg1_2 ).ToList();
return;
}
class CrossMapping {
public IDictionary<Type, Type> TypeMapping { get; private set; }
public IDictionary<MemberInfo, MemberInfo> MemberMapping { get; private set; }
public CrossMapping() {
this.TypeMapping = new Dictionary<Type, Type>();
this.MemberMapping = new Dictionary<MemberInfo, MemberInfo>();
}
}
static Expression MutateExpression( Expression originalExpression, CrossMapping mapping ) {
var ret = MutateExpression(
originalExpression: originalExpression,
parameterExpressions: null,
mapping: mapping
);
return ret;
}
static Expression MutateExpression( Expression originalExpression, IList<ParameterExpression> parameterExpressions, CrossMapping mapping ) {
Expression ret;
if ( null == originalExpression ) {
ret = null;
}
else if ( originalExpression is LambdaExpression ) {
ret = MutateLambdaExpression( (LambdaExpression)originalExpression, parameterExpressions, mapping );
}
else if ( originalExpression is BinaryExpression ) {
ret = MutateBinaryExpression( (BinaryExpression)originalExpression, parameterExpressions, mapping );
}
else if ( originalExpression is ParameterExpression ) {
ret = MutateParameterExpression( (ParameterExpression)originalExpression, parameterExpressions, mapping );
}
else if ( originalExpression is MemberExpression ) {
ret = MutateMemberExpression( (MemberExpression)originalExpression, parameterExpressions, mapping );
}
else if ( originalExpression is ConstantExpression ) {
ret = MutateConstantExpression( (ConstantExpression)originalExpression, parameterExpressions, mapping );
}
else {
throw new NotImplementedException();
}
return ret;
}
static Type MutateType( Type originalType, IDictionary<Type, Type> typeMapping ) {
if ( null == originalType ) { return null; }
Type ret;
typeMapping.TryGetValue( originalType, out ret );
if ( null == ret ) { ret = originalType; }
return ret;
}
static MemberInfo MutateMember( MemberInfo originalMember, IDictionary<MemberInfo, MemberInfo> memberMapping ) {
if ( null == originalMember ) { return null; }
MemberInfo ret;
memberMapping.TryGetValue( originalMember, out ret );
if ( null == ret ) { ret = originalMember; }
return ret;
}
static LambdaExpression MutateLambdaExpression( LambdaExpression originalExpression, IList<ParameterExpression> parameterExpressions, CrossMapping mapping ) {
if ( null == originalExpression ) { return null; }
var newParameters = (from p in originalExpression.Parameters
let np = MutateParameterExpression( p, parameterExpressions, mapping )
select np).ToArray();
var newBody = MutateExpression( originalExpression.Body, newParameters, mapping );
var newType = MutateType( originalExpression.Type, mapping.TypeMapping );
var ret = Expression.Lambda(
delegateType: newType,
body: newBody,
name: originalExpression.Name,
tailCall: originalExpression.TailCall,
parameters: newParameters
);
return ret;
}
static BinaryExpression MutateBinaryExpression( BinaryExpression originalExpression, IList<ParameterExpression> parameterExpressions, CrossMapping mapping ) {
if ( null == originalExpression ) { return null; }
var newExprConversion = MutateExpression( originalExpression.Conversion, parameterExpressions, mapping );
var newExprLambdaConversion = (LambdaExpression)newExprConversion;
var newExprLeft = MutateExpression( originalExpression.Left, parameterExpressions, mapping );
var newExprRigth = MutateExpression( originalExpression.Right, parameterExpressions, mapping );
var newType = MutateType( originalExpression.Type, mapping.TypeMapping );
var newMember = MutateMember( originalExpression.Method, mapping.MemberMapping);
var newMethod = (MethodInfo)newMember;
var ret = Expression.MakeBinary(
binaryType: originalExpression.NodeType,
left: newExprLeft,
right: newExprRigth,
liftToNull: originalExpression.IsLiftedToNull,
method: newMethod,
conversion: newExprLambdaConversion
);
return ret;
}
static ParameterExpression MutateParameterExpression( ParameterExpression originalExpresion, IList<ParameterExpression> parameterExpressions, CrossMapping mapping ) {
if ( null == originalExpresion ) { return null; }
ParameterExpression ret = null;
if ( null != parameterExpressions ) {
ret = (from p in parameterExpressions
where p.Name == originalExpresion.Name
select p).FirstOrDefault();
}
if ( null == ret ) {
var newType = MutateType( originalExpresion.Type, mapping.TypeMapping );
ret = Expression.Parameter( newType, originalExpresion.Name );
}
return ret;
}
static MemberExpression MutateMemberExpression( MemberExpression originalExpression, IList<ParameterExpression> parameterExpressions, CrossMapping mapping ) {
if ( null == originalExpression ) { return null; }
var newExpression = MutateExpression( originalExpression.Expression, parameterExpressions, mapping );
var newMember = MutateMember( originalExpression.Member, mapping.MemberMapping );
var ret = Expression.MakeMemberAccess(
expression: newExpression,
member: newMember
);
return ret;
}
static ConstantExpression MutateConstantExpression( ConstantExpression originalExpression, IList<ParameterExpression> parameterExpressions, CrossMapping mapping ) {
if ( null == originalExpression ) { return null; }
var newType = MutateType( originalExpression.Type, mapping.TypeMapping );
var newValue = originalExpression.Value;
var ret = Expression.Constant(
value: newValue,
type: newType
);
return ret;
}
}
}
Jon's own answer above is great, so I expanded it to handle method calls, constant expressions, etc. so that now it works also for expressions such as:
x => x.SubObjects
.AsQueryable()
.SelectMany(y => y.GrandChildObjects)
.Any(z => z.Value == 3)
I also did away with the ExpressionTreeExplorer since the only thing we need are the ParameterExpressions.
Here's the code (Update: Clear the cache when done converting)
public class ExpressionTargetTypeMutator : ExpressionVisitor
{
private readonly Func<Type, Type> typeConverter;
private readonly Dictionary<Expression, Expression> _convertedExpressions
= new Dictionary<Expression, Expression>();
public ExpressionTargetTypeMutator(Func<Type, Type> typeConverter)
{
this.typeConverter = typeConverter;
}
// Clear the ParameterExpression cache between calls to Visit.
// Not thread safe, but you can probably fix it easily.
public override Expression Visit(Expression node)
{
bool outermostCall = false;
if (false == _isVisiting)
{
this._isVisiting = true;
outermostCall = true;
}
try
{
return base.Visit(node);
}
finally
{
if (outermostCall)
{
this._isVisiting = false;
_convertedExpressions.Clear();
}
}
}
protected override Expression VisitMember(MemberExpression node)
{
var sourceType = node.Member.ReflectedType;
var targetType = this.typeConverter(sourceType);
var converted = Expression.MakeMemberAccess(
base.Visit(node.Expression),
targetType.GetProperty(node.Member.Name));
return converted;
}
protected override Expression VisitParameter(ParameterExpression node)
{
Expression converted;
if (false == _convertedExpressions.TryGetValue(node, out converted))
{
var sourceType = node.Type;
var targetType = this.typeConverter(sourceType);
converted = Expression.Parameter(targetType, node.Name);
_convertedExpressions.Add(node, converted);
}
return converted;
}
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (node.Method.IsGenericMethod)
{
var convertedTypeArguments = node.Method.GetGenericArguments()
.Select(this.typeConverter)
.ToArray();
var genericMethodDefinition = node.Method.GetGenericMethodDefinition();
var newMethod = genericMethodDefinition.MakeGenericMethod(convertedTypeArguments);
return Expression.Call(newMethod, node.Arguments.Select(this.Visit));
}
return base.VisitMethodCall(node);
}
protected override Expression VisitConstant(ConstantExpression node)
{
var valueExpression = node.Value as Expression;
if (null != valueExpression)
{
return Expression.Constant(this.Visit(valueExpression));
}
return base.VisitConstant(node);
}
protected override Expression VisitLambda<T>(Expression<T> node)
{
return Expression.Lambda(this.Visit(node.Body), node.Name, node.TailCall, node.Parameters.Select(x => (ParameterExpression)this.VisitParameter(x)));
}
}
Doesn't ExecuteTypedList accomplish what you want to do? SubSonic will populate your DTO's/POCOs. From Rob Connery's blog:
ExecuteTypedList<> tries to match the
names of the returned columns to the
names of the properties of the
passed-in type. In this example they
match exactly - and that's not
completely real world. You can get
around this by aliasing the columns -
in the same way you'd alias a SQL
call:
return Northwind.DB.Select("ProductID as 'ID'", "ProductName as 'Name'", "UnitPrice as 'Price'")
.From<Northwind.Product>().ExecuteTypedList<Product>();
Here's the link to Rob's Writing Decoupled, Testable code with SubSonic 2.1
I think Linq-To-Sql will produce the desirable SQL if you do your queries correctly. In this case, using IQueryable and deferred execution you can avoid returning all ActiveRecord.Widget records.
IEnumerable<DataContract.Widget> GetMany(
Func<DataContract.Widget, bool> predicate)
{
// get Widgets
IQueryable<DataContract.Widget> qry = dc.Widgets.Select(w => TODO: CONVERT_TO_DataContract.Widget);
return qry.Where(predicate);
}