Unable to compile decompiled code via dotPeek - c#

I'm looking at the following decompiled code and it wouldn't compile because of memberExpression1 and memberExpression2. How do I put the puzzles back, so it compiles?
using System.Globalization;
using System.Linq.Expressions;
using System.Reflection;
namespace Test;
public class RoutingDetail
{
protected RoutingDetail(IEnumerable<KeyValuePair<string, string>> routingParameters)
{
RoutingKeyParameters = routingParameters;
}
public IEnumerable<KeyValuePair<string, string>> RoutingKeyParameters { get; }
}
public class RoutingDetail<TMessage> : RoutingDetail
{
public RoutingDetail()
: base(new Dictionary<string, string>())
{
}
private RoutingDetail(IEnumerable<KeyValuePair<string, string>> routingParameters)
: base(routingParameters)
{
}
public RoutingDetail<TMessage> WithRoutingHeader(
KeyValuePair<string, string> routingHeader)
{
var dictionary = RoutingKeyParameters.ToDictionary(k => k.Key, k => k.Value);
dictionary.Add(routingHeader.Key, routingHeader.Value);
return new RoutingDetail<TMessage>(dictionary);
}
public RoutingDetail<TMessage> WithRoutingHeader<TRoutingValue>(
Expression<Func<TMessage, TRoutingValue>> property,
TRoutingValue value)
{
var dictionary = RoutingKeyParameters.ToDictionary(k => k.Key, k => k.Value);
var nameValueFromFunc = GetValidPropertyNameValueFromFunc(property, value);
dictionary.Add(nameValueFromFunc.Key, nameValueFromFunc.Value);
return new RoutingDetail<TMessage>(dictionary);
}
private KeyValuePair<string, string> GetValidPropertyNameValueFromFunc<TProperty>(
Expression<Func<TMessage, TProperty>> property,
TProperty value)
{
if (property.Body is not MemberExpression memberExpression1)
{
if (property.Body.NodeType is ExpressionType.Convert or ExpressionType.ConvertChecked &&
property.Body is UnaryExpression body)
{
memberExpression1 = body.Operand as MemberExpression;
}
if (memberExpression1 == null)
{
throw new ArgumentException($"Expression '{property}' refers to a method, not a property.");
}
}
var member1 = memberExpression1.Member as PropertyInfo;
var key = (object)member1 != null
? member1.Name
: throw new ArgumentException($"Expression '{property}' refers to a field, not a property.");
if (CustomAttributeExtensions.GetCustomAttributes(member1, typeof(RouteOnAttribute), true)
.FirstOrDefault() is not RouteOnAttribute routeOnAttribute1)
{
throw new ArgumentException(
$"Property {key} does not have the RouteOn attribute, and will not be used when routing. ");
}
memberExpression2 = memberExpression1;
while (memberExpression2.Expression is MemberExpression memberExpression2)
{
var member2 = memberExpression2.Member as PropertyInfo;
if ((object)member2 == null)
{
throw new ArgumentException($"Expression '{property}' refers to a field, not a property.");
}
key = $"{member2.Name}.{key}";
if (CustomAttributeExtensions.GetCustomAttributes(member2, typeof(RouteOnAttribute), true)
.FirstOrDefault() is not RouteOnAttribute routeOnAttribute2)
{
throw new ArgumentException(
$"Property {member1.Name} does not have the RouteOn attribute, and will not be used when routing. ");
}
if (!routeOnAttribute2.UseInnerProperties)
{
throw new ArgumentException(
$"Property {member1.Name} does not have the UseInnerProperties attribute set, and will not be used when routing.");
}
}
return new KeyValuePair<string, string>(key,
string.Format(CultureInfo.InvariantCulture, routeOnAttribute1.FormatString, value));
}
}
[AttributeUsage(AttributeTargets.Property)]
public class RouteOnAttribute : Attribute
{
public RouteOnAttribute()
{
FormatString = "{0}";
UseInnerProperties = false;
}
public string FormatString { get; set; }
public bool UseInnerProperties { get; set; }
}

Convert if (property.Body is not MemberExpression memberExpression1){ ... } to
var memberExpression1 = property.Body as MemberExpression;
if (memberExpression1 is null) {
if (property.Body.NodeType is ExpressionType.Convert or ExpressionType.ConvertChecked &&
property.Body is UnaryExpression body) {
memberExpression1 = body.Operand as MemberExpression;
}
if (memberExpression1 == null) {
throw new ArgumentException($"Expression '{property}' refers to a method, not a property.");
}
}
Convert
memberExpression2 = memberExpression1;
while (memberExpression2.Expression is MemberExpression memberExpression2) { ... }
to
MemberExpression memberExpression2 = memberExpression1;
while (memberExpression2.Expression is MemberExpression) { ... }
It is not advisable to copy/paste code that you don't understand.

Related

Problem evaluation conditional Lambdaexpressions

I have a problem evaluation conditional lambdaexpressions, using System.Linq.Expressions.
Here the full example Code:
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace ExpressionTree
{
class Program<TType>
{
public TType Data { get; set; }
public void RegisterLambda<TProp>(Expression<Func<TType, TProp>> expression)
{
if (expression.Body.NodeType == ExpressionType.Conditional)
{
ConditionalExpression condExp = (ConditionalExpression)expression.Body;
var condTest = condExp.Test;
var paramExpression = Expression.Parameter(typeof(TType), "x");
var lambda = Expression.Lambda(condTest, paramExpression);
var deleg = lambda.Compile(); // this will raise an InvalidOperationException: variable 'x' of type 'ExpressionTree.DataObject'
// referenced from scope '', but it is not defined
var testResult = deleg.DynamicInvoke(Data);
}
if (expression.Body.NodeType == ExpressionType.MemberAccess)
{
var pi = expression.GetPropertyInfo();
Console.WriteLine(pi.GetValue(Data));
}
if (expression.Body.NodeType == ExpressionType.Constant)
{
ConstantExpression ce = (ConstantExpression)expression.Body;
Console.WriteLine(ce.Value);
}
}
}
class Start
{
static void Main(string[] args)
{
Program<DataObject> P = new Program<DataObject>();
P.Data = new DataObject();
P.Data.DisplayName = "1234567890";
P.RegisterLambda(x => x.DisplayName);
P.RegisterLambda(x => x.DisplayName.Length <= 3 ? "Foobar" : x.DisplayName);
P.RegisterLambda<string>(something => "abc");
}
}
class DataObject
{
public string DisplayName { get; set; }
}
static class Extensions
{
public static PropertyInfo GetPropertyInfo(this LambdaExpression expression)
{
if (expression.Body is UnaryExpression unaryExp)
{
if (unaryExp.Operand is MemberExpression memberExp)
{
return (PropertyInfo)memberExp.Member;
}
}
else if (expression.Body is MemberExpression memberExp)
{
return (PropertyInfo)memberExp.Member;
}
throw new Exception();
}
}
}
While calling the 2nd RegisterLambda, which is a ternery conditional lambda. The execution fails on the Compile()-line. Does anyone have a clue to solve that problem? I have tried many combinations using ParameterExpression
Thanks in advance!

Retrieving nested PropertyInfo via expressions

I'm trying to create a function where I can pass in an expression to say which properties I'm interested in. I have it working for top level properties but not for nested properties.
Example model
public class Foo {
public string Name { get; set; }
public List<Foo> List { get; set; }
}
What I have so far
private PropertyInfo GetPropertyInfo<TModel>(Expression<Func<TModel, object>> selector)
{
if (selector.NodeType != ExpressionType.Lambda)
{
throw new ArgumentException("Selector must be lambda expression", nameof(selector));
}
var lambda = (LambdaExpression)selector;
var memberExpression = ExtractMemberExpression(lambda.Body);
if (memberExpression == null)
throw new ArgumentException("Selector must be member access expression", nameof(selector));
if (memberExpression.Member.DeclaringType == null)
{
throw new InvalidOperationException("Property does not have declaring type");
}
return memberExpression.Member.DeclaringType.GetProperty(memberExpression.Member.Name);
}
private static MemberExpression ExtractMemberExpression(Expression expression)
{
if (expression.NodeType == ExpressionType.MemberAccess)
{
return ((MemberExpression)expression);
}
if (expression.NodeType == ExpressionType.Convert)
{
var operand = ((UnaryExpression)expression).Operand;
return ExtractMemberExpression(operand);
}
return null;
}
So:
GetPropertyInfo<Foo>(x => x.Name); // works
GetPropertyInfo<Foo>(x => x.List.Select(y => y.Name); <-- how do I get this?
I'm looking for a way to pick any property from a complex object.
You need to extend your ExtractMemberExpression just a bit to accept Select call expression:
private MemberExpression ExtractMemberExpression(Expression expression) {
if (expression.NodeType == ExpressionType.MemberAccess) {
return ((MemberExpression) expression);
}
if (expression.NodeType == ExpressionType.Convert) {
var operand = ((UnaryExpression) expression).Operand;
return ExtractMemberExpression(operand);
}
if (expression.NodeType == ExpressionType.Lambda) {
return ExtractMemberExpression(((LambdaExpression) expression).Body);
}
if (expression.NodeType == ExpressionType.Call) {
var call = (MethodCallExpression) expression;
// any method named Select with 2 parameters will do
if (call.Method.Name == "Select" && call.Arguments.Count == 2) {
return ExtractMemberExpression(call.Arguments[1]);
}
}
return null;
}

Reading lambda expression [duplicate]

Is there a better way to get the Property name when passed in via a lambda expression?
Here is what i currently have.
eg.
GetSortingInfo<User>(u => u.UserId);
It worked by casting it as a memberexpression only when the property was a string. because not all properties are strings i had to use object but then it would return a unaryexpression for those.
public static RouteValueDictionary GetInfo<T>(this HtmlHelper html,
Expression<Func<T, object>> action) where T : class
{
var expression = GetMemberInfo(action);
string name = expression.Member.Name;
return GetInfo(html, name);
}
private static MemberExpression GetMemberInfo(Expression method)
{
LambdaExpression lambda = method as LambdaExpression;
if (lambda == null)
throw new ArgumentNullException("method");
MemberExpression memberExpr = null;
if (lambda.Body.NodeType == ExpressionType.Convert)
{
memberExpr =
((UnaryExpression)lambda.Body).Operand as MemberExpression;
}
else if (lambda.Body.NodeType == ExpressionType.MemberAccess)
{
memberExpr = lambda.Body as MemberExpression;
}
if (memberExpr == null)
throw new ArgumentException("method");
return memberExpr;
}
I recently did a very similar thing to make a type safe OnPropertyChanged method.
Here's a method that'll return the PropertyInfo object for the expression. It throws an exception if the expression is not a property.
public PropertyInfo GetPropertyInfo<TSource, TProperty>(
TSource source,
Expression<Func<TSource, TProperty>> propertyLambda)
{
Type type = typeof(TSource);
MemberExpression member = propertyLambda.Body as MemberExpression;
if (member == null)
throw new ArgumentException(string.Format(
"Expression '{0}' refers to a method, not a property.",
propertyLambda.ToString()));
PropertyInfo propInfo = member.Member as PropertyInfo;
if (propInfo == null)
throw new ArgumentException(string.Format(
"Expression '{0}' refers to a field, not a property.",
propertyLambda.ToString()));
if (type != propInfo.ReflectedType &&
!type.IsSubclassOf(propInfo.ReflectedType))
throw new ArgumentException(string.Format(
"Expression '{0}' refers to a property that is not from type {1}.",
propertyLambda.ToString(),
type));
return propInfo;
}
The source parameter is used so the compiler can do type inference on the method call. You can do the following
var propertyInfo = GetPropertyInfo(someUserObject, u => u.UserID);
I found another way you can do it was to have the source and property strongly typed and explicitly infer the input for the lambda. Not sure if that is correct terminology but here is the result.
public static RouteValueDictionary GetInfo<T,P>(this HtmlHelper html, Expression<Func<T, P>> action) where T : class
{
var expression = (MemberExpression)action.Body;
string name = expression.Member.Name;
return GetInfo(html, name);
}
And then call it like so.
GetInfo((User u) => u.UserId);
and voila it works.
I was playing around with the same thing and worked this up. It's not fully tested but seems to handle the issue with value types (the unaryexpression issue you ran into)
public static string GetName(Expression<Func<object>> exp)
{
MemberExpression body = exp.Body as MemberExpression;
if (body == null) {
UnaryExpression ubody = (UnaryExpression)exp.Body;
body = ubody.Operand as MemberExpression;
}
return body.Member.Name;
}
public string GetName<TSource, TField>(Expression<Func<TSource, TField>> Field)
{
return (Field.Body as MemberExpression ?? ((UnaryExpression)Field.Body).Operand as MemberExpression).Member.Name;
}
This handles member and unary expressions. The difference being that you will get a UnaryExpression if your expression represents a value type whereas you will get a MemberExpression if your expression represents a reference type. Everything can be cast to an object, but value types must be boxed. This is why the UnaryExpression exists. Reference.
For the sakes of readability (#Jowen), here's an expanded equivalent:
public string GetName<TSource, TField>(Expression<Func<TSource, TField>> Field)
{
if (object.Equals(Field, null))
{
throw new NullReferenceException("Field is required");
}
MemberExpression expr = null;
if (Field.Body is MemberExpression)
{
expr = (MemberExpression)Field.Body;
}
else if (Field.Body is UnaryExpression)
{
expr = (MemberExpression)((UnaryExpression)Field.Body).Operand;
}
else
{
const string Format = "Expression '{0}' not supported.";
string message = string.Format(Format, Field);
throw new ArgumentException(message, "Field");
}
return expr.Member.Name;
}
With C# 7 pattern matching:
public static string GetMemberName<T>(this Expression<T> expression)
{
switch (expression.Body)
{
case MemberExpression m:
return m.Member.Name;
case UnaryExpression u when u.Operand is MemberExpression m:
return m.Member.Name;
default:
throw new NotImplementedException(expression.GetType().ToString());
}
}
Example:
public static RouteValueDictionary GetInfo<T>(this HtmlHelper html,
Expression<Func<T, object>> action) where T : class
{
var name = action.GetMemberName();
return GetInfo(html, name);
}
[Update] C# 8 pattern matching:
public static string GetMemberName<T>(this Expression<T> expression) => expression.Body switch
{
MemberExpression m => m.Member.Name,
UnaryExpression u when u.Operand is MemberExpression m => m.Member.Name,
_ => throw new NotImplementedException(expression.GetType().ToString())
};
now in C# 6 you can simply use nameof like this nameof(User.UserId)
which has many benefits, among them is that this is done at compile time, not runtime.
https://msdn.microsoft.com/en-us/magazine/dn802602.aspx
This is a general implementation to get the string name of fields/properties/indexers/methods/extension methods/delegates of struct/class/interface/delegate/array. I have tested with combinations of static/instance and non-generic/generic variants.
//involves recursion
public static string GetMemberName(this LambdaExpression memberSelector)
{
Func<Expression, string> nameSelector = null; //recursive func
nameSelector = e => //or move the entire thing to a separate recursive method
{
switch (e.NodeType)
{
case ExpressionType.Parameter:
return ((ParameterExpression)e).Name;
case ExpressionType.MemberAccess:
return ((MemberExpression)e).Member.Name;
case ExpressionType.Call:
return ((MethodCallExpression)e).Method.Name;
case ExpressionType.Convert:
case ExpressionType.ConvertChecked:
return nameSelector(((UnaryExpression)e).Operand);
case ExpressionType.Invoke:
return nameSelector(((InvocationExpression)e).Expression);
case ExpressionType.ArrayLength:
return "Length";
default:
throw new Exception("not a proper member selector");
}
};
return nameSelector(memberSelector.Body);
}
This thing can be written in a simple while loop too:
//iteration based
public static string GetMemberName(this LambdaExpression memberSelector)
{
var currentExpression = memberSelector.Body;
while (true)
{
switch (currentExpression.NodeType)
{
case ExpressionType.Parameter:
return ((ParameterExpression)currentExpression).Name;
case ExpressionType.MemberAccess:
return ((MemberExpression)currentExpression).Member.Name;
case ExpressionType.Call:
return ((MethodCallExpression)currentExpression).Method.Name;
case ExpressionType.Convert:
case ExpressionType.ConvertChecked:
currentExpression = ((UnaryExpression)currentExpression).Operand;
break;
case ExpressionType.Invoke:
currentExpression = ((InvocationExpression)currentExpression).Expression;
break;
case ExpressionType.ArrayLength:
return "Length";
default:
throw new Exception("not a proper member selector");
}
}
}
I like the recursive approach, though the second one might be easier to read. One can call it like:
someExpr = x => x.Property.ExtensionMethod()[0]; //or
someExpr = x => Static.Method().Field; //or
someExpr = x => VoidMethod(); //or
someExpr = () => localVariable; //or
someExpr = x => x; //or
someExpr = x => (Type)x; //or
someExpr = () => Array[0].Delegate(null); //etc
string name = someExpr.GetMemberName();
to print the last member.
Note:
In case of chained expressions like A.B.C, "C" is returned.
This doesn't work with consts, array indexers or enums (impossible to cover all cases).
There's an edge case when it comes to Array.Length. While 'Length' is exposed as a property, you can't use it in any of the previously proposed solutions.
using Contract = System.Diagnostics.Contracts.Contract;
using Exprs = System.Linq.Expressions;
static string PropertyNameFromMemberExpr(Exprs.MemberExpression expr)
{
return expr.Member.Name;
}
static string PropertyNameFromUnaryExpr(Exprs.UnaryExpression expr)
{
if (expr.NodeType == Exprs.ExpressionType.ArrayLength)
return "Length";
var mem_expr = expr.Operand as Exprs.MemberExpression;
return PropertyNameFromMemberExpr(mem_expr);
}
static string PropertyNameFromLambdaExpr(Exprs.LambdaExpression expr)
{
if (expr.Body is Exprs.MemberExpression) return PropertyNameFromMemberExpr(expr.Body as Exprs.MemberExpression);
else if (expr.Body is Exprs.UnaryExpression) return PropertyNameFromUnaryExpr(expr.Body as Exprs.UnaryExpression);
throw new NotSupportedException();
}
public static string PropertyNameFromExpr<TProp>(Exprs.Expression<Func<TProp>> expr)
{
Contract.Requires<ArgumentNullException>(expr != null);
Contract.Requires<ArgumentException>(expr.Body is Exprs.MemberExpression || expr.Body is Exprs.UnaryExpression);
return PropertyNameFromLambdaExpr(expr);
}
public static string PropertyNameFromExpr<T, TProp>(Exprs.Expression<Func<T, TProp>> expr)
{
Contract.Requires<ArgumentNullException>(expr != null);
Contract.Requires<ArgumentException>(expr.Body is Exprs.MemberExpression || expr.Body is Exprs.UnaryExpression);
return PropertyNameFromLambdaExpr(expr);
}
Now example usage:
int[] someArray = new int[1];
Console.WriteLine(PropertyNameFromExpr( () => someArray.Length ));
If PropertyNameFromUnaryExpr didn't check for ArrayLength, "someArray" would be printed to the console (compiler seems to generate direct access to the backing Length field, as an optimization, even in Debug, thus the special case).
Here's an update to method proposed by Cameron. The first parameter is not required.
public PropertyInfo GetPropertyInfo<TSource, TProperty>(
Expression<Func<TSource, TProperty>> propertyLambda)
{
Type type = typeof(TSource);
MemberExpression member = propertyLambda.Body as MemberExpression;
if (member == null)
throw new ArgumentException(string.Format(
"Expression '{0}' refers to a method, not a property.",
propertyLambda.ToString()));
PropertyInfo propInfo = member.Member as PropertyInfo;
if (propInfo == null)
throw new ArgumentException(string.Format(
"Expression '{0}' refers to a field, not a property.",
propertyLambda.ToString()));
if (type != propInfo.ReflectedType &&
!type.IsSubclassOf(propInfo.ReflectedType))
throw new ArgumentException(string.Format(
"Expresion '{0}' refers to a property that is not from type {1}.",
propertyLambda.ToString(),
type));
return propInfo;
}
You can do the following:
var propertyInfo = GetPropertyInfo<SomeType>(u => u.UserID);
var propertyInfo = GetPropertyInfo((SomeType u) => u.UserID);
Extension methods:
public static PropertyInfo GetPropertyInfo<TSource, TProperty>(this TSource source,
Expression<Func<TSource, TProperty>> propertyLambda) where TSource : class
{
return GetPropertyInfo(propertyLambda);
}
public static string NameOfProperty<TSource, TProperty>(this TSource source,
Expression<Func<TSource, TProperty>> propertyLambda) where TSource : class
{
PropertyInfo prodInfo = GetPropertyInfo(propertyLambda);
return prodInfo.Name;
}
You can:
SomeType someInstance = null;
string propName = someInstance.NameOfProperty(i => i.Length);
PropertyInfo propInfo = someInstance.GetPropertyInfo(i => i.Length);
I've found that some of the suggested answers which drill down into the MemberExpression/UnaryExpression don't capture nested/subproperties.
ex) o => o.Thing1.Thing2 returns Thing1 rather than Thing1.Thing2.
This distinction is important if you're trying to work with EntityFramework DbSet.Include(...).
I've found that just parsing the Expression.ToString() seems to work fine, and comparatively quickly. I compared it against the UnaryExpression version, and even getting ToString off of the Member/UnaryExpression to see if that was faster, but the difference was negligible. Please correct me if this is a terrible idea.
The Extension Method
/// <summary>
/// Given an expression, extract the listed property name; similar to reflection but with familiar LINQ+lambdas. Technique #via https://stackoverflow.com/a/16647343/1037948
/// </summary>
/// <remarks>Cheats and uses the tostring output -- Should consult performance differences</remarks>
/// <typeparam name="TModel">the model type to extract property names</typeparam>
/// <typeparam name="TValue">the value type of the expected property</typeparam>
/// <param name="propertySelector">expression that just selects a model property to be turned into a string</param>
/// <param name="delimiter">Expression toString delimiter to split from lambda param</param>
/// <param name="endTrim">Sometimes the Expression toString contains a method call, something like "Convert(x)", so we need to strip the closing part from the end</param>
/// <returns>indicated property name</returns>
public static string GetPropertyName<TModel, TValue>(this Expression<Func<TModel, TValue>> propertySelector, char delimiter = '.', char endTrim = ')') {
var asString = propertySelector.ToString(); // gives you: "o => o.Whatever"
var firstDelim = asString.IndexOf(delimiter); // make sure there is a beginning property indicator; the "." in "o.Whatever" -- this may not be necessary?
return firstDelim < 0
? asString
: asString.Substring(firstDelim+1).TrimEnd(endTrim);
}//-- fn GetPropertyNameExtended
(Checking for the delimiter might even be overkill)
Demo (LinqPad)
Demonstration + Comparison code -- https://gist.github.com/zaus/6992590
I"m using an extension method for pre C# 6 projects and the nameof() for those targeting C# 6.
public static class MiscExtentions
{
public static string NameOf<TModel, TProperty>(this object #object, Expression<Func<TModel, TProperty>> propertyExpression)
{
var expression = propertyExpression.Body as MemberExpression;
if (expression == null)
{
throw new ArgumentException("Expression is not a property.");
}
return expression.Member.Name;
}
}
And i call it like:
public class MyClass
{
public int Property1 { get; set; }
public string Property2 { get; set; }
public int[] Property3 { get; set; }
public Subclass Property4 { get; set; }
public Subclass[] Property5 { get; set; }
}
public class Subclass
{
public int PropertyA { get; set; }
public string PropertyB { get; set; }
}
// result is Property1
this.NameOf((MyClass o) => o.Property1);
// result is Property2
this.NameOf((MyClass o) => o.Property2);
// result is Property3
this.NameOf((MyClass o) => o.Property3);
// result is Property4
this.NameOf((MyClass o) => o.Property4);
// result is PropertyB
this.NameOf((MyClass o) => o.Property4.PropertyB);
// result is Property5
this.NameOf((MyClass o) => o.Property5);
It works fine with both fields and properties.
Well, there's no need to call .Name.ToString(), but broadly that is about it, yes. The only consideration you might need is whether x.Foo.Bar should return "Foo", "Bar", or an exception - i.e. do you need to iterate at all.
(re comment) for more on flexible sorting, see here.
This might be optimal
public static string GetPropertyName<TResult>(Expression<Func<TResult>> expr)
{
var memberAccess = expr.Body as MemberExpression;
var propertyInfo = memberAccess?.Member as PropertyInfo;
var propertyName = propertyInfo?.Name;
return propertyName;
}
I leave this function if you want to get multiples fields:
/// <summary>
/// Get properties separated by , (Ex: to invoke 'd => new { d.FirstName, d.LastName }')
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="exp"></param>
/// <returns></returns>
public static string GetFields<T>(Expression<Func<T, object>> exp)
{
MemberExpression body = exp.Body as MemberExpression;
var fields = new List<string>();
if (body == null)
{
NewExpression ubody = exp.Body as NewExpression;
if (ubody != null)
foreach (var arg in ubody.Arguments)
{
fields.Add((arg as MemberExpression).Member.Name);
}
}
return string.Join(",", fields);
}
I created an extension method on ObjectStateEntry to be able to flag properties (of Entity Framework POCO classes) as modified in a type safe manner, since the default method only accepts a string. Here's my way of getting the name from the property:
public static void SetModifiedProperty<T>(this System.Data.Objects.ObjectStateEntry state, Expression<Func<T>> action)
{
var body = (MemberExpression)action.Body;
string propertyName = body.Member.Name;
state.SetModifiedProperty(propertyName);
}
I have done the INotifyPropertyChanged implementation similar to the method below. Here the properties are stored in a dictionary in the base class shown below. It is of course not always desirable to use inheritance, but for view models I think it is acceptable and gives very clean property references in the view model classes.
public class PhotoDetailsViewModel
: PropertyChangedNotifierBase<PhotoDetailsViewModel>
{
public bool IsLoading
{
get { return GetValue(x => x.IsLoading); }
set { SetPropertyValue(x => x.IsLoading, value); }
}
public string PendingOperation
{
get { return GetValue(x => x.PendingOperation); }
set { SetPropertyValue(x => x.PendingOperation, value); }
}
public PhotoViewModel Photo
{
get { return GetValue(x => x.Photo); }
set { SetPropertyValue(x => x.Photo, value); }
}
}
The somewhat more complex base class is shown below. It handles the translation from lambda expression to property name. Note that the properties are really pseudo properties since only the names are used. But it will appear transparent to the view model and references to the properties on the view model.
public class PropertyChangedNotifierBase<T> : INotifyPropertyChanged
{
readonly Dictionary<string, object> _properties = new Dictionary<string, object>();
protected U GetValue<U>(Expression<Func<T, U>> property)
{
var propertyName = GetPropertyName(property);
return GetValue<U>(propertyName);
}
private U GetValue<U>(string propertyName)
{
object value;
if (!_properties.TryGetValue(propertyName, out value))
{
return default(U);
}
return (U)value;
}
protected void SetPropertyValue<U>(Expression<Func<T, U>> property, U value)
{
var propertyName = GetPropertyName(property);
var oldValue = GetValue<U>(propertyName);
if (Object.ReferenceEquals(oldValue, value))
{
return;
}
_properties[propertyName] = value;
RaisePropertyChangedEvent(propertyName);
}
protected void RaisePropertyChangedEvent<U>(Expression<Func<T, U>> property)
{
var name = GetPropertyName(property);
RaisePropertyChangedEvent(name);
}
protected void RaisePropertyChangedEvent(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private static string GetPropertyName<U>(Expression<Func<T, U>> property)
{
if (property == null)
{
throw new NullReferenceException("property");
}
var lambda = property as LambdaExpression;
var memberAssignment = (MemberExpression) lambda.Body;
return memberAssignment.Member.Name;
}
public event PropertyChangedEventHandler PropertyChanged;
}
This is another answer:
public static string GetPropertyName<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression)
{
var metaData = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
return metaData.PropertyName;
}
Here is another way to get the PropertyInfo based off this answer. It eliminates the need for an object instance.
/// <summary>
/// Get metadata of property referenced by expression. Type constrained.
/// </summary>
public static PropertyInfo GetPropertyInfo<TSource, TProperty>(Expression<Func<TSource, TProperty>> propertyLambda)
{
return GetPropertyInfo((LambdaExpression) propertyLambda);
}
/// <summary>
/// Get metadata of property referenced by expression.
/// </summary>
public static PropertyInfo GetPropertyInfo(LambdaExpression propertyLambda)
{
// https://stackoverflow.com/questions/671968/retrieving-property-name-from-lambda-expression
MemberExpression member = propertyLambda.Body as MemberExpression;
if (member == null)
throw new ArgumentException(string.Format(
"Expression '{0}' refers to a method, not a property.",
propertyLambda.ToString()));
PropertyInfo propInfo = member.Member as PropertyInfo;
if (propInfo == null)
throw new ArgumentException(string.Format(
"Expression '{0}' refers to a field, not a property.",
propertyLambda.ToString()));
if(propertyLambda.Parameters.Count() == 0)
throw new ArgumentException(String.Format(
"Expression '{0}' does not have any parameters. A property expression needs to have at least 1 parameter.",
propertyLambda.ToString()));
var type = propertyLambda.Parameters[0].Type;
if (type != propInfo.ReflectedType &&
!type.IsSubclassOf(propInfo.ReflectedType))
throw new ArgumentException(String.Format(
"Expression '{0}' refers to a property that is not from type {1}.",
propertyLambda.ToString(),
type));
return propInfo;
}
It can be called like so:
var propertyInfo = GetPropertyInfo((User u) => u.UserID);
GetPropetyAccess() is available if you can refer efcore.
using Microsoft.EntityFrameworkCore.Infrastructure;
var propertyInfo = lambda.GetPropetyAccess(); //PropertyInfo
var propertyName = propertyInfo.Name;
I've updated #Cameron's answer to include some safety checks against Convert typed lambda expressions:
PropertyInfo GetPropertyName<TSource, TProperty>(
Expression<Func<TSource, TProperty>> propertyLambda)
{
var body = propertyLambda.Body;
if (!(body is MemberExpression member)
&& !(body is UnaryExpression unary
&& (member = unary.Operand as MemberExpression) != null))
throw new ArgumentException($"Expression '{propertyLambda}' " +
"does not refer to a property.");
if (!(member.Member is PropertyInfo propInfo))
throw new ArgumentException($"Expression '{propertyLambda}' " +
"refers to a field, not a property.");
var type = typeof(TSource);
if (!propInfo.DeclaringType.GetTypeInfo().IsAssignableFrom(type.GetTypeInfo()))
throw new ArgumentException($"Expresion '{propertyLambda}' " +
"refers to a property that is not from type '{type}'.");
return propInfo;
}
Starting with .NET 4.0 you can use ExpressionVisitor to find properties:
class ExprVisitor : ExpressionVisitor {
public bool IsFound { get; private set; }
public string MemberName { get; private set; }
public Type MemberType { get; private set; }
protected override Expression VisitMember(MemberExpression node) {
if (!IsFound && node.Member.MemberType == MemberTypes.Property) {
IsFound = true;
MemberName = node.Member.Name;
MemberType = node.Type;
}
return base.VisitMember(node);
}
}
Here is how you use this visitor:
var visitor = new ExprVisitor();
visitor.Visit(expr);
if (visitor.IsFound) {
Console.WriteLine("First property in the expression tree: Name={0}, Type={1}", visitor.MemberName, visitor.MemberType.FullName);
} else {
Console.WriteLine("No properties found.");
}
static void Main(string[] args)
{
var prop = GetPropertyInfo<MyDto>(_ => _.MyProperty);
MyDto dto = new MyDto();
dto.MyProperty = 666;
var value = prop.GetValue(dto);
// value == 666
}
class MyDto
{
public int MyProperty { get; set; }
}
public static PropertyInfo GetPropertyInfo<TSource>(Expression<Func<TSource, object>> propertyLambda)
{
Type type = typeof(TSource);
var member = propertyLambda.Body as MemberExpression;
if (member == null)
{
var unary = propertyLambda.Body as UnaryExpression;
if (unary != null)
{
member = unary.Operand as MemberExpression;
}
}
if (member == null)
{
throw new ArgumentException(string.Format("Expression '{0}' refers to a method, not a property.",
propertyLambda.ToString()));
}
var propInfo = member.Member as PropertyInfo;
if (propInfo == null)
{
throw new ArgumentException(string.Format("Expression '{0}' refers to a field, not a property.",
propertyLambda.ToString()));
}
if (type != propInfo.ReflectedType && !type.IsSubclassOf(propInfo.ReflectedType))
{
throw new ArgumentException(string.Format("Expression '{0}' refers to a property that is not from type {1}.",
propertyLambda.ToString(), type));
}
return propInfo;
}
Assuming (TModel as class)
Expression<Func<TModel, TValue>> expression
retrieve the property's name with
expression.GetPropertyInfo().Name;
The extension function:
public static PropertyInfo GetPropertyInfo<TType, TReturn>(this Expression<Func<TType, TReturn>> property)
{
LambdaExpression lambda = property;
var memberExpression = lambda.Body is UnaryExpression expression
? (MemberExpression)expression.Operand
: (MemberExpression)lambda.Body;
return (PropertyInfo)memberExpression.Member;
}

Expression and Automapper

Somewhere in the internet I found below class which I am using to convert Expression<Func<T, bool>> from DTO to Domain:
public class EvaluateVariableVisitor<TEntity, TDto> : ExpressionVisitor
{
private readonly ParameterExpression _dtoParameter;
private readonly ParameterExpression _entityParameter;
private readonly IMapper _mapper;
public EvaluateVariableVisitor()
{
_entityParameter = Expression.Parameter(typeof(TEntity));
_dtoParameter = Expression.Parameter(typeof(TDto));
_mapper = AutoMapperConfig.Initialize();
}
protected override Expression VisitMember(MemberExpression node)
{
try
{
//change dto to entity type.
if (node.Expression.Type == _dtoParameter.Type)
{
var reducedExpression = Visit(node.Expression);
//TypeMap typeMap = Mapper.Configuration.FindTypeMapFor<TDto, TEntity>();
TypeMap typeMap = _mapper.ConfigurationProvider.FindTypeMapFor<TDto, TEntity>();
//find the correct name of the property in the destination object by using the name of the source objekt
string destinationPropertyName = typeMap.GetPropertyMaps() //GetCustomPropertyMaps()
.Where(propertyMap => propertyMap.SourceMember.Name == node.Member.Name)
.Select(propertyMap => propertyMap.DestinationProperty.Name).FirstOrDefault();
//find the correct name of the property in the destination object by using the name of the source objekt
//string destinationPropertyName = typeMap.GetPropertyMaps() //GetCustomPropertyMaps()
// .Where(propertyMap => propertyMap.SourceMember.Name == node.Member.Name)
// .Select(propertyMap => propertyMap.DestinationProperty.Name).Single();
var newMember = _entityParameter.Type.GetMember(destinationPropertyName).First();
return Expression.MakeMemberAccess(reducedExpression, newMember);
}
//Recurse down to see if we can simplify...
var expression = Visit(node.Expression);
//If we've ended up with a constant, and it's a property or a field,
//we can simplify ourselves to a constant
var constantExpression = expression as ConstantExpression;
if (constantExpression != null)
{
object container = constantExpression.Value;
object value = null;
var memberAsFieldInfo = node.Member as FieldInfo;
var memberAsPropertyInfo = node.Member as PropertyInfo;
if (memberAsFieldInfo != null)
{
value = memberAsFieldInfo.GetValue(container);
}
if (memberAsPropertyInfo != null)
{
value = memberAsPropertyInfo.GetValue(container, null);
}
if (value != null)
{
return Expression.Constant(value);
}
}
return base.VisitMember(node);
}
catch (System.Exception exc)
{
var ex = exc.Message;
throw;
}
}
//change type from dto to entity --> otherwise the generated expression tree would throw an exception, because of missmatching types(Dto can't be used in Entity expression).
protected override Expression VisitParameter(ParameterExpression node)
{
return node.Type == _dtoParameter.Type ? _entityParameter : node;
}
}
It was working great but I faced an issue when I had property of the same type inside my DTO:
public class InventoryApplicationDto
{
public int Id { get; set; }
public string Name{ get; set; }
public InventoryApplicationDto ParentApplication { get; set; }
}
First of all I had to change:
string destinationPropertyName = typeMap.GetPropertyMaps()
.Where(propertyMap => propertyMap.SourceMember.Name == node.Member.Name)
.Select(propertyMap => propertyMap.DestinationProperty.Name).Single();
To:
string destinationPropertyName = typeMap.GetPropertyMaps()
.Where(propertyMap => propertyMap.SourceMember.Name == node.Member.Name)
.Select(propertyMap => propertyMap.DestinationProperty.Name).FirstOrDefault();
My problem is that I am getting error:
System.ArgumentException: Property 'Int32 ID' is not defined for type
'InventoryApplicationDto'
on line:
return Expression.MakeMemberAccess(reducedExpression, newMember);
Is MakeMemberAccess method case sensitive or there is another issue?

get and set object property using Expression trees instead of Reflection

I would like to dynamically get and set an objects properties as follows:
public class Person
{
public string Name {get; set; }
}
public class Testing
{
public void Run()
{
var p = new Person();
SetValue(p, "Name", "Henry");
var name = GetValue(p, "Name");
}
}
Please could I get help creating the GetValue and SetValue methods using dynamic method (or expression trees)?
I am intending to save compiled expressions in a dictionary, to speed up future get/set calls.
Do you really want to use expression trees? for this simple scenario I would try to compile directly into a DynamicMethod using Reflection.Emit API's by getting an IL Generator. But .. for expression trees, i wrote a helper for you:
public class PropertyManager : DynamicObject
{
private static Dictionary<Type, Dictionary<string, GetterAndSetter>> _compiledProperties = new Dictionary<Type, Dictionary<string, GetterAndSetter>>();
private static Object _compiledPropertiesLockObject = new object();
private class GetterAndSetter
{
public Action<object, Object> Setter { get; set; }
public Func<Object, Object> Getter { get; set; }
}
private Object _object;
private Type _objectType;
private PropertyManager(Object o)
{
_object = o;
_objectType = o.GetType();
}
public static dynamic Wrap(Object o)
{
if (o == null)
return null; // null reference will be thrown
var type = o.GetType();
EnsurePropertySettersAndGettersForType(type);
return new PropertyManager(o) as dynamic;
}
private static void EnsurePropertySettersAndGettersForType(Type type)
{
if (false == _compiledProperties.ContainsKey(type))
lock (_compiledPropertiesLockObject)
if (false == _compiledProperties.ContainsKey(type))
{
Dictionary<string, GetterAndSetter> _getterAndSetters;
_compiledProperties[type] = _getterAndSetters = new Dictionary<string, GetterAndSetter>();
var properties = type.GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic);
foreach (var property in properties)
{
var getterAndSetter = new GetterAndSetter();
_getterAndSetters[property.Name] = getterAndSetter;
// burn getter and setter
if (property.CanRead)
{
// burn getter
var param = Expression.Parameter(typeof(object), "param");
Expression propExpression = Expression.Convert(Expression.Property(Expression.Convert(param, type), property), typeof(object));
var lambda = Expression.Lambda(propExpression, new[] { param });
var compiled = lambda.Compile() as Func<object, object>;
getterAndSetter.Getter = compiled;
}
if (property.CanWrite)
{
var thisParam = Expression.Parameter(typeof(Object), "this");
var theValue = Expression.Parameter(typeof(Object), "value");
var isValueType = property.PropertyType.IsClass == false && property.PropertyType.IsInterface == false;
Expression valueExpression;
if (isValueType)
valueExpression = Expression.Unbox(theValue, property.PropertyType);
else
valueExpression = Expression.Convert(theValue, property.PropertyType);
var thisExpression = Expression.Property (Expression.Convert(thisParam, type), property);
Expression body = Expression.Assign(thisExpression, valueExpression);
var block = Expression.Block(new[]
{
body,
Expression.Empty ()
});
var lambda = Expression.Lambda(block, thisParam, theValue);
getterAndSetter.Setter = lambda.Compile() as Action<Object, Object>;
}
}
}
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
var memberName = binder.Name;
result = null;
Dictionary<string, GetterAndSetter> dict;
GetterAndSetter getterAndSetter;
if (false == _compiledProperties.TryGetValue(_objectType, out dict)
|| false == dict.TryGetValue(memberName, out getterAndSetter))
{
return false;
}
if (getterAndSetter.Getter == null)
throw new NotSupportedException("The property has no getter!");
result = getterAndSetter.Getter(_object);
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
var memberName = binder.Name;
Dictionary<string, GetterAndSetter> dict;
GetterAndSetter getterAndSetter;
if (false == _compiledProperties.TryGetValue(_objectType, out dict)
|| false == dict.TryGetValue(memberName, out getterAndSetter))
{
return false;
}
if (getterAndSetter.Setter == null)
throw new NotSupportedException("The property has no getter!");
getterAndSetter.Setter(_object, value);
return true;
}
}
And this is how you can use it:
Person p = new Person();
p.Name = "mama";
var wrapped = PropertyManager.Wrap(p);
var val = wrapped.Name; // here we are using our compiled method ...
It is very obvious that you can extract my compilation logic to use strings instead of letting DLR giving the property name for you :)

Categories

Resources