Retrieving an Object Owner from an Property Expression

I am working on a small amount of code that has the ultimate goal of allowing you to use a property expression to set the value of a property with similar syntax to pass 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 a value value.

I use the following code to get an open property object:

 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; } 

Thus, when using the like () => Object.Property property in the expression, return an instance of "Object". I'm a little new to using property expressions, and there seem to be many different ways of doing things, but I want to expand on what I have so far, so given an expression such as () => Foo.Bar.Baz it will give the bar, not Foo. I always need the last contained object in the expression.

Any ideas? Thanks in advance.

+4
source share
2 answers

What you need to do is go through the chain of properties to the outermost object. The following example is more likely to explain itself and show that the extension method will work for both the chain and the 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)); } } 
+3
source

If your project is an MVC 5 project and you have a link to the System.Web.Mvc assembly, you can use the following:

Some time ago, I wrote an extension method that makes it easy to create a drop-down list with multiple selections (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 named PropertyName and another property called Container which is a reference to an instance of the container object.

0
source

All Articles