Getting ConstantExpression.Value when actual value wrapped into DisplayClass because of closure - c#

Below is a simple demonstration code of my problem.
[TestClass]
public class ExpressionTests
{
[TestMethod]
public void TestParam()
{
Search<Student>(s => s.Id == 1L);
GetStudent(1L);
}
private void GetStudent(long id)
{
Search<Student>(s => s.Id == id);
}
private void Search<T>(Expression<Func<T, bool>> filter)
{
var visitor = new MyExpressionVisitor();
visitor.Visit(filter);
}
}
public class MyExpressionVisitor : ExpressionVisitor
{
protected override Expression VisitConstant(ConstantExpression node)
{
Assert.AreEqual(1L, node.Value);
return base.VisitConstant(node);
}
}
TestParam method causes VisitConstant to be invoked on two different paths:
1. TestParam -> Search -> VisitConstant
In this execution path constant expression (1L) passed to Search method is a real constant value. Here, everything is OK, assert succeeds as expected. When VisitConstant is invoked via first path node.Value.GetType() is Int64 and its .Value is 1L.
2. TestParam -> GetStudent -> Search -> VisitConstant
In this execution path constant expression (id: 1L), is taken by GetStudent as an argument and passed to Search method inside a closure.
Problem
The problem is on the second execution path. When VisitConstant is invoked via second path node.Value.GetType() is MyProject.Tests.ExpressionTests+<>c__DisplayClass0 and this class has a public field named id (same as GetStudent method's argument) which has the value of 1L.
Question
How can I get id value in second path? I know about closures, what a DisplayClass is and why it is created at compile time etc. I am only interested in getting its field value.
One thing I can think of is, via reflection. With something like below but it does not seem neat.
node.Value.GetType().GetFields()[0].GetValue(node.Value);
Bonus Problem
While playing with the code for gettting id value I changed VisitConstant method like below (which will not solve my problem though) and get an exception saying "'object' does not contain a definition for 'id'"
Bonus Question
As dynamics are resolved at runtime and DisplayClass is created at compile time, why cannot we access its fields with dynamic? While below code works, I expected that code would work too.
var st = new {Id = 1L};
object o = st;
dynamic dy = o;
Assert.AreEqual(1L, dy.Id);

VisitConstant will not help here, as it receives a compiler constructed ConstantExpression that uses object of private anonymous class to store values lambda was closed over (The DisplayClassxxx)
Instead, we should override VisitMember method and inspect its MemberExpression that already has ConstantExpression as an inner Expression.
Here is working test with little reflection.
[TestClass]
public class UnitTest2
{
[TestMethod]
public void TestMethod2()
{
Search<Student>(s => s.Id == 1L);
GetStudent(1L);
}
private void GetStudent(long id)
{
Search<Student>(s => s.Id == id);
}
private void Search<T>(Expression<Func<T, bool>> filter)
{
var visitor = new MyExpressionVisitor2();
visitor.Visit(filter.Body);
}
}
//ExpressionVisitor
public class MyExpressionVisitor2 : ExpressionVisitor
{
protected override Expression VisitMember(MemberExpression node)
{
switch (node.Expression.NodeType)
{
case ExpressionType.Constant:
case ExpressionType.MemberAccess:
{
var cleanNode = GetMemberConstant(node);
//Test
Assert.AreEqual(1L, cleanNode.Value);
return cleanNode;
}
default:
{
return base.VisitMember(node);
}
}
}
private static ConstantExpression GetMemberConstant(MemberExpression node)
{
object value;
if (node.Member.MemberType == MemberTypes.Field)
{
value = GetFieldValue(node);
}
else if (node.Member.MemberType == MemberTypes.Property)
{
value = GetPropertyValue(node);
}
else
{
throw new NotSupportedException();
}
return Expression.Constant(value, node.Type);
}
private static object GetFieldValue(MemberExpression node)
{
var fieldInfo = (FieldInfo)node.Member;
var instance = (node.Expression == null) ? null : TryEvaluate(node.Expression).Value;
return fieldInfo.GetValue(instance);
}
private static object GetPropertyValue(MemberExpression node)
{
var propertyInfo = (PropertyInfo)node.Member;
var instance = (node.Expression == null) ? null : TryEvaluate(node.Expression).Value;
return propertyInfo.GetValue(instance, null);
}
private static ConstantExpression TryEvaluate(Expression expression)
{
if (expression.NodeType == ExpressionType.Constant)
{
return (ConstantExpression)expression;
}
throw new NotSupportedException();
}
}

Here is an article that explains how to do it, and includes code that does it. Basically, what you can do is to create an expression that represents just that subexpression, compile it to a delegate and then execute that delegate. (The article also explains how to identify subexpressions that can be evaluated, but I guess you're not interested in that.)
Using the code from the article, modifying your code to the following will work:
private void Search<T>(Expression<Func<T, bool>> filter)
{
new MyExpressionVisitor().Visit(Evaluator.PartialEval(filter));
}
As dynamics are resolved at runtime and DisplayClass is created at compile time, why cannot we access its fields with dynamic?
Because that DisplayClass is a private class nested inside ExpressionTests, so code inside MyExpressionVisitor can't access its members.
If you make MyExpressionVisitor a nested class inside ExpressionTests, dynamic will start working on the DisplayClass.
Anonymous types don't behave this way, because they are not emitted as nested private types.

Related

C# inspect and modify Expression<Func<T, bool>>?

My generic repository method is declared like this:
public virtual async Task<List<T>> GetMany(Expression<Func<T, bool>> filterExpression)
I would like to investigate that filterExpression and check if T is of a certain type AND if so, check if there's a filter for a property called ShardKey. Also, if the type checks out but the filter is not present, I would like to add it.
How can I achieve this?
Using an ExpressionVisitor you can search the Expression tree to determine if a reference to the desired property is made. If you wanted to be more sure, you could test to see if it is in a certain type of logical test as well.
If you don't find the desired reference, you can add a parent test to the filterExpression.
Here is an example, assuming ShardKey is an int:
public virtual async Task<List<T>> GetMany<T>(Expression<Func<T, bool>> filterExpression) {
if (typeof(T) == typeof(CertainType) && !filterExpression.ReferencesMember<CertainType>("ShardKey")) {
var skTest = Expression.Equal(Expression.PropertyOrField(filterExpression.Parameters[0], "ShardKey"), Expression.Constant(7));
filterExpression = Expression.Lambda<Func<T,bool>>(Expression.AndAlso(skTest, filterExpression.Body), filterExpression.Parameters);
}
Here is the ExpressionVisitor implementation:
public static class ExpressionExt {
public static bool ReferencesMember<T>(this Expression ex, string memberName) {
var mi = typeof(T).GetMember(memberName);
if (mi.Length != 1)
throw new ArgumentException($"{memberName} ambiguous or not found for {typeof(T).Name}");
var mf = new MemberFinder(mi[0]);
mf.Visit(ex);
return mf.Found;
}
class MemberFinder : ExpressionVisitor {
MemberInfo mi;
public bool Found = false;
public MemberFinder(MemberInfo _mi) => mi = _mi;
[return: NotNullIfNotNull("node")]
public override Expression Visit(Expression node) {
if (Found)
return node;
else
return base.Visit(node);
}
protected override Expression VisitMember(MemberExpression me) {
if (me.Member == mi)
Found = true;
return me;
}
}
}

How can i get a string from linq expression?

I have this method and parameter.
void SomeMethod(Expression<Func<Products, bool>> where)
I call this method like this;
int i = 9;
SomeMethod(x=>x.Id==i)
And I want it to produce this string;
"x=>x.Id==9"
If I just print out the above expression as it is it will give me this string:
"x => (x.Id == value(isTakibi.WepApp.Controllers.HomeController+<>c__DisplayClass4_0).i)"
but I need "x.Id == 9". I need to evaluate the value of the variable i so that the result will be "x.id==9".
The way to simplify an expression in general is to compile it and execute the compiled delegate. Now you can't do that for any expression that still has any parameter expressions in it, because you don't know what the parameter's value will be (yet). This means we have two fundamental steps, first, determine which of the sub-expressions in our tree actually contain a parameter somewhere within that sub-tree, then evaluate all of the ones that don't.
So the first step is to determine which expressions contain a parameter within them. To do that we create an expression visitor that has a field indicating whether it's currently inside of a sub-tree with a parameter which then recursively checks its children, then checks itself, and then combines the results, adding all parameterless expressions into a collection while along the way.
private class ParameterlessExpressionSearcher : ExpressionVisitor
{
public HashSet<Expression> ParameterlessExpressions { get; } = new HashSet<Expression>();
private bool containsParameter = false;
public override Expression Visit(Expression node)
{
bool originalContainsParameter = containsParameter;
containsParameter = false;
base.Visit(node);
if (!containsParameter)
{
if (node?.NodeType == ExpressionType.Parameter)
containsParameter = true;
else
ParameterlessExpressions.Add(node);
}
containsParameter |= originalContainsParameter;
return node;
}
}
Next to evaluate the sub-expressions that have no parameter we need another visitor. This one just has to check if the expression is in the set of expressions we found with the previous visitor, and if so, compiles that expression into a parameterless delegate and executes it, otherwise it'll check its children to see if any of them can be replaced.
private class ParameterlessExpressionEvaluator : ExpressionVisitor
{
private HashSet<Expression> parameterlessExpressions;
public ParameterlessExpressionEvaluator(HashSet<Expression> parameterlessExpressions)
{
this.parameterlessExpressions = parameterlessExpressions;
}
public override Expression Visit(Expression node)
{
if (parameterlessExpressions.Contains(node))
return Evaluate(node);
else
return base.Visit(node);
}
private Expression Evaluate(Expression node)
{
if (node.NodeType == ExpressionType.Constant)
{
return node;
}
object value = Expression.Lambda(node).Compile().DynamicInvoke();
return Expression.Constant(value, node.Type);
}
}
Now we just need a simple method to first execute the first searcher, then the second, and return the results, and provide an overload that casts the result to a generic expression:
public static class ExpressionExtensions
{
public static Expression Simplify(this Expression expression)
{
var searcher = new ParameterlessExpressionSearcher();
searcher.Visit(expression);
return new ParameterlessExpressionEvaluator(searcher.ParameterlessExpressions).Visit(expression);
}
public static Expression<T> Simplify<T>(this Expression<T> expression)
{
return (Expression<T>)Simplify((Expression)expression);
}
//all previously shown code goes here
}
Now you can write:
Expression<Func<Products, bool>> e = x => x.Id == i;
e = e.Simplify();
Console.WriteLine(e);
And it'll print:
"x => (x.Id == 9)"
Alternatively, if you just want to evaluate one particular expression, and you're willing to change how you write the expression in the first place to accommodate, this answer shows how you can write a method to indicate what sub-expressions should be evaluated, and to evaluate just those expressions. That would be useful if you want to evaluate some things and not others.
.ToString() works for me:
void SomeMethod(Expression<Func<Product, bool>> where)
{
Console.WriteLine(where.ToString());
}
When calling with
SomeMethod(x=>x.Id==9);
Outputs:
x => (x.Id == 9)
As others have noted, you can get some resemblance of the original expression by calling ToString() on it, but this only works for very simple implementations and won't work well with closures. The c# compiler does a lot of "magic" to make things like closures work in expressions, and the "<>DisplayClass" stuff you're seeing is the result of that. You'd need to implement a custom visitor to go through the expression and write out the c# (essentially a reverse compiler) to get back to the original.
It would probably look something like the following stub:
public sealed class ExpressionWriterVisitor : ExpressionVisitor
{
private TextWriter _writer;
public ExpressionWriterVisitor(TextWriter writer)
{
_writer = writer;
}
protected override Expression VisitParameter(ParameterExpression node)
{
_writer.Write(node.Name);
return node;
}
protected override Expression VisitLambda<T>(Expression<T> node)
{
_writer.Write('(');
_writer.Write(string.Join(',', node.Parameters.Select(param => param.Name)));
_writer.Write(')');
_writer.Write("=>");
Visit(node.Body);
return node;
}
protected override Expression VisitConditional(ConditionalExpression node)
{
Visit(node.Test);
_writer.Write('?');
Visit(node.IfTrue);
_writer.Write(':');
Visit(node.IfFalse);
return node;
}
protected override Expression VisitBinary(BinaryExpression node)
{
Visit(node.Left);
_writer.Write(GetOperator(node.NodeType));
Visit(node.Right);
return node;
}
protected override Expression VisitMember(MemberExpression node)
{
// Closures are represented as a constant object with fields representing each closed over value.
// This gets and prints the value of that closure.
if (node.Member is FieldInfo fieldInfo && node.Expression is ConstantExpression constExpr)
{
WriteConstantValue(fieldInfo.GetValue(constExpr.Value));
}
else
{
Visit(node.Expression);
_writer.Write('.');
_writer.Write(node.Member.Name);
}
return node;
}
protected override Expression VisitConstant(ConstantExpression node)
{
WriteConstantValue(node.Value);
return node;
}
private void WriteConstantValue(object obj)
{
switch (obj)
{
case string str:
_writer.Write('"');
_writer.Write(str);
_writer.Write('"');
break;
default:
_writer.Write(obj);
break;
}
}
private static string GetOperator(ExpressionType type)
{
switch (type)
{
case ExpressionType.Equal:
return "==";
case ExpressionType.Not:
return "!";
case ExpressionType.NotEqual:
return "!==";
case ExpressionType.GreaterThan:
return ">";
case ExpressionType.GreaterThanOrEqual:
return ">=";
case ExpressionType.LessThan:
return "<";
case ExpressionType.LessThanOrEqual:
return "<=";
case ExpressionType.Or:
return "|";
case ExpressionType.OrElse:
return "||";
case ExpressionType.And:
return "&";
case ExpressionType.AndAlso:
return "&&";
case ExpressionType.Add:
return "+";
case ExpressionType.AddAssign:
return "+=";
case ExpressionType.Subtract:
return "-";
case ExpressionType.SubtractAssign:
return "-=";
default:
return "???";
}
}
}
Note in VisitMember where it has some logic to extract the value out of a closure.
This will print out "(x)=>x.Id==9":
static void Main(string[] args)
{
var i = 9;
Expression<Func<Product, bool>> where = x => x.Id == i;
new ExpressionWriterVisitor(Console.Out).Visit(where);
}
In your example, your code is doing the right thing. The problem is that the variable i is not declared const. The expression has to assume that the value of i might change before it is getting called. This code gives you the result you expect: const int i = 9;
Unfortunately, this approach probably won't work for method caching for the same reason. Your code also doesn't have a way to ensure that i doesn't change between the time the expression is declared and the time when it is evaluated.
You could try writing an ExpressionVisitor that tries to find closures like that and evaluate them, but I've never tried it myself.

Expression API throws exception: variable 'x' of type 'x' referenced from scope '', but it is not defined

public class Program
{
private static void Main()
{
ContrivedComparer.Compare<Person>(person => person.Name == "Calvin");
}
}
public class Person
{
public string Name { get; set; }
}
public class ContrivedComparer
{
// this likely looks highly ill-advised out of context but this is contrived.
public static readonly object comparatePerson = new Person { Name = "Ted" };
public static void Compare<TComparate>(Expression<Func<TComparate, bool>> predicate)
{
if (predicate.Compile()((TComparate)comparatePerson)) return;
var expression = (BinaryExpression)predicate.Body;
var actual = Expression.Lambda(expression.Left).Compile().DynamicInvoke();
var expected = Expression.Lambda(expression.Right).Compile().DynamicInvoke();
}
}
I expect actual to have the same value as the left operand and for expected to have the same value as the the right operand.
However, this code throws an InvalidOperationException with the following message:
variable 'person' of type 'Person' referenced from scope '', but it is not defined.
How can I resolve this exception?
You aren't passing the parameter to the lambda that you're creating the second time, the way that you are the first time. You need to indicate that there is a parameter when constructing the lambda and pass in the value when invoking it.
public static void Compare<TComparate>(Expression<Func<TComparate, bool>> predicate)
{
if (predicate.Compile()((TComparate)comparatePerson)) return;
var expression = (BinaryExpression)predicate.Body;
var actual = Expression.Lambda(expression.Left, predicate.Parameters)
.Compile().DynamicInvoke(comparatePerson);
var expected = Expression.Lambda(expression.Right, predicate.Parameters)
.Compile().DynamicInvoke(comparatePerson);
}

Getting the owning object of a property from a Property Expression

I'm working on a bit of code which has an end purpose of letting you use a property expression to set the value of a property with similar syntax to passing a variable as an out or ref parameter.
Something along the lines of:
public static foo(()=>Object.property, value);
And Object.Property will be assigned the value of value.
I'm using the following code to get the owining object of the property:
public static object GetOwningObject<T>(this Expression<Func<T>> #this)
{
var memberExpression = #this.Body as MemberExpression;
if (memberExpression != null)
{
var fieldExpression = memberExpression.Expression as MemberExpression;
if (fieldExpression != null)
{
var constExpression = fieldExpression.Expression as ConstantExpression;
var field = fieldExpression.Member as FieldInfo;
if (constExpression != null) if (field != null) return field.GetValue(constExpression.Value);
}
}
return null;
}
So this would, when used on a property expression like ()=>Object.Property, give back the instance of 'Object'. I'm somewhat new to using property expressions, and there seems to be many different ways to accomplish things, but I want to extend what I have so far, so that given an expression such as ()=>Foo.Bar.Baz it will give the Bar, not Foo. I always want the last containing object in the expression.
Any ideas? Thanks in advance.
What you have to do is to traverse through property chain to the most outer object.
The sample below is rather self explanatory and shows that the extension method would work for chained fields as well as properties:
class Foo
{
public Bar Bar { get; set; }
}
class Bar
{
public string Baz { get; set; }
}
class FooWithField
{
public BarWithField BarField;
}
class BarWithField
{
public string BazField;
}
public static class LambdaExtensions
{
public static object GetRootObject<T>(this Expression<Func<T>> expression)
{
var propertyAccessExpression = expression.Body as MemberExpression;
if (propertyAccessExpression == null)
return null;
//go up through property/field chain
while (propertyAccessExpression.Expression is MemberExpression)
propertyAccessExpression = (MemberExpression)propertyAccessExpression.Expression;
//the last expression suppose to be a constant expression referring to captured variable ...
var rootObjectConstantExpression = propertyAccessExpression.Expression as ConstantExpression;
if (rootObjectConstantExpression == null)
return null;
//... which is stored in a field of generated class that holds all captured variables.
var fieldInfo = propertyAccessExpression.Member as FieldInfo;
if (fieldInfo != null)
return fieldInfo.GetValue(rootObjectConstantExpression.Value);
return null;
}
}
[TestFixture]
public class Program
{
[Test]
public void Should_find_root_element_by_property_chain()
{
var foo = new Foo { Bar = new Bar { Baz = "text" } };
Expression<Func<string>> expression = () => foo.Bar.Baz;
Assert.That(expression.GetRootObject(), Is.SameAs(foo));
}
[Test]
public void Should_find_root_element_by_field_chain()
{
var foo = new FooWithField { BarField = new BarWithField { BazField = "text" } };
Expression<Func<string>> expression = () => foo.BarField.BazField;
Assert.That(expression.GetRootObject(), Is.SameAs(foo));
}
}
If your project is an MVC 5 project and you have a reference to the assembly System.Web.Mvc You could use the following:
Some time ago I wrote an extension method to easily create a multiselect dropdown (based on bootstrap 4) and it looked something like this:
public static MvcHtmlString MultiSelectFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
{
/*The challenge I faced here was that the expression you passed could very well be nested, so in order overcome this, I decompiled the dll to see how MVC does it, and I found this piece of code.*/
string expressionText = System.Web.Mvc.ExpressionHelper.GetExpressionText((LambdaExpression)expression);
System.Web.Mvc.ModelMetadata metadata = System.Web.Mvc.ModelMetadata.FromStringExpression(expressionText, htmlHelper.ViewData);
}
The metadata object has a Property called PropertyName and another property called Container which is a reference to the instance of the container object.

Using anonymous type in lambda to map or partially clone any number of an object's properties

I want to make a cloning or mapping function where I can specify (once) which properties to copy to the target object.
The whole point here was a usage that looks something like this
(inspired by using GroupBy),
var botchedOrder = db.BuildingOrders.Find(id);
var redo = db.BuildingOrders.Create();
botchedOrder.MapTo(redo, x => new { x.BasePrice, x.FileAttachments, x.Notes });
This is over my head, but I was guessing at something like this,
public static void MapTo<TObject, TProps>(
this TObject source,
TObject target,
Action<TProps> properties) //?
{
//copy the defined properties from source to target somehow?
//originally I thought I could make an array of delegates..
}
If this works then I can more easily handle different sets of properties in different ways when I am explicitly cloning or mapping objects. I'd like to stick w/ .NET to do this.
EDIT: forgot to indicate void
I'd recommend looking at AutoMapper since it kind of solves same kinds of problems. Also, different ORM sources (like Massive, Petapoco etc.) all have some implementations (since they need to map data from a DB to an object).
Basically, it works either using reflection - to iterate over given fields/properties and set them to another object (also via refleciton). There are approaches to dynamic method generation using IL Generator or Expression Trees that wrap all the reflection code into one nice and fast method generated and compiled at runtime (thus giving performance boost).
Another way to do would be using dynamics or Dictionaries instead of anonymous class - that would remove "anonimity" problem and would just require to map specific keys on to properties - may be done with reflection again (using for example [MappingAttribute("Key")] for distinction - attribute is a fiction, just to give a general idea).
This is the most succinct form I can think of without dropping down to reflection, but it does involve repeating property names, so I'm not sure if it's exactly what you want.
public static void MapTo<TObject>(this TObject source, TObject target, params Action<TObject, TObject>[] properties)
{
foreach (var property in properties)
{
property(source, target);
}
}
Called like:
void Copy(FooBar source, FooBar target)
{
source.MapTo(target, (s,t) => t.Foo = s.Foo,
(s,t) => t.Bar = s.Bar,
(t,s) => t.Baz = s.Baz);
}
class FooBar
{
public string Foo { get; set; }
public string Bar { get; set; }
public string Baz { get; set; }
}
However, it's more verbose that just doing:
void Copy(FooBar source, FooBar target)
{
target.Foo = source.Foo;
target.Bar = source.Bar;
target.Baz = source.Baz;
}
Is there anything else going on in your copy that make the last example invalid? If not, I would just keep it simple and go for that.
Here is a basic dynamic mapper I wrote. I used a slightly different approach as I extended object and instead of specifying the properties, used ignore properties.
public static class ObjectExtensions
{
public static void CopyFrom(this object Instance, object Source)
{
ObjectExtensions.CopyFrom(Instance, Source, false, null);
}
public static void CopyFrom(this object Instance,
object Source,
IEnumerable<string> IgnoreProperties)
{
ObjectExtensions.CopyFrom(Instance, Source, false, IgnoreProperties);
}
public static void CopyFrom(this object Instance,
object Source,
bool ThrowOnPropertyMismatch,
IEnumerable<string> IgnoreProperties)
{
Type sourceType = Source.GetType();
BindingFlags publicInstanceFlags = BindingFlags.Public
| BindingFlags.Instance;
PropertyInfo[] sourceProperties =
sourceType.GetProperties(publicInstanceFlags);
Type instanceType = Instance.GetType();
foreach (PropertyInfo sourceProperty in sourceProperties)
{
if (IgnoreProperties == null
|| (IgnoreProperties.Count() > 0
&& !IgnoreProperties.Contains(sourceProperty.Name)))
{
PropertyInfo instanceProperty =
instanceType.GetProperty(sourceProperty.Name, publicInstanceFlags);
if (instanceProperty != null
&& instanceProperty.PropertyType == sourceProperty.PropertyType
&& instanceProperty.GetSetMethod() != null
&& instanceProperty.GetSetMethod().IsPublic)
{
instanceProperty.SetValue(Instance,
sourceProperty.GetValue(Source, null),
null);
}
else
if (ThrowOnPropertyMismatch
&& instanceProperty.PropertyType != sourceProperty.PropertyType)
{
throw new InvalidCastException(
string.Format("Unable to cast source {0}.{1} to destination {2}.{3}.",
Source.GetType().Name,
sourceProperty.Name,
Instance.GetType().Name,
instanceProperty.Name));
}
}
}
}
A common way of doing this is by using expression trees which can represent the way certain types should map to one another. A primitive stripped down example of such is:
public static void MapTo<TInput, TOutput>(this TInput input, TOutput output, Expression<Func<TInput, TOutput, bool>> expression)
where TInput : class
where TOutput : class
{
if (expression == null)
throw new ArgumentNullException("expression");
Stack<Expression> unhandeledExpressions = new Stack<Expression>();
unhandeledExpressions.Push(expression.Body);
while (unhandeledExpressions.Any())
{
Expression unhandeledExpression = unhandeledExpressions.Pop();
switch (unhandeledExpression.NodeType)
{
case ExpressionType.AndAlso:
{
BinaryExpression binaryExpression = (BinaryExpression)unhandeledExpression;
unhandeledExpressions.Push(binaryExpression.Left);
unhandeledExpressions.Push(binaryExpression.Right);
}
break;
case ExpressionType.Equal:
{
BinaryExpression binaryExpression = (BinaryExpression)unhandeledExpression;
MemberExpression leftArgumentExpression = binaryExpression.Left as MemberExpression;
MemberExpression rightArgumentExpression = binaryExpression.Right as MemberExpression;
if (leftArgumentExpression == null || rightArgumentExpression == null)
throw new InvalidOperationException("Can only map to member expressions");
output.GetType().GetProperty(leftArgumentExpression.Member.Name).SetValue(
output, input.GetType().GetProperty(rightArgumentExpression.Member.Name).GetValue(input, null), null);
}
break;
default:
throw new InvalidOperationException("Expression type not supported");
}
}
}
}
which can be used in the following way:
class SourceType
{
public string Name { get; set; }
public int Number { get; set; }
}
class DestinationType
{
public string CustName { get; set; }
public int Age { get; set; }
}
class Program
{
static void Main(string[] args)
{
var source = new SourceType()
{
Name = "Test",
Number = 22
};
var destination = new DestinationType();
source.MapTo(destination, (src, dst) => dst.CustName == src.Name && dst.Age == src.Number);
bool assert = source.Name == destination.CustName && source.Number == destination.Age;
}
}
The advantage of this approach is that this allows you to define your own mapping 'language' which you can make as complex/extensive as you want.
Still I recommend you to use a pre-built solution like AutoFaq or AutoMapper. Good luck
ok, I think I have something working using an Expression and Reflection.
thing1.MapTo(thing2, x => new { x.Prop1, x.Prop2 });
and
public static void MapTo<T, P>(
this T source, T target, Expression<Func<T, P>> expr)
{
(expr.Body as NewExpression).Members.ToList()
.ForEach(m => source.Copy(target, m.Name));
}
public static void Copy<T>(this T source, T target, string prop)
{
var p = typeof(T).GetProperty(prop);
p.SetValue(target, p.GetValue(source, null), null);
}
I'm not sure the methods have the best names but it's a start and it allows the usage I was hoping for. Any problems with this approach?

Categories

Resources