Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting Parent Member from Expression

I have an Expression that is used to get a list of items from my Model for my View. What I want to do, is given the Expression for the List, can I go backwards upward on the Expression Tree one level to get the parent node?

Let's say this is my view model:

public class MyModel {
   public MyClass myClass { get; set;}
}

...

public class MyClass {
   public List<string> MyList { get; set;}
}

I have an HtmlHelper that takes an Expression as follows to render a list to the page:

public static MvcHtmlString RenderList(this HtmlHelper<TModel> html, Expression<Func<TModel, IEnumerable<TItem>>> dataExpression)
{   
     var list = dataExpression.Compile()(html.ViewData.Model);

     ...

     return MvcHtmlString.Create(...);
}

I call my helper as follows:

@model MyNamespace.Models.MyModel
@Html.RenderList(m => m.myClass.MyList)

This all works fine, my question is, can I take the given Expression which points to the List<string> and get it's parent node (MyClass) then compile it against my Model to get it's value. So the equivalent of this Expression:

m => m.myClass

Here's what I have tried:

// This gets the correct expression
var exp = ((MemberExpression)this._dataExpression.Body).Expression;
// Create a parameter representing the type of the Model ?
var parameter = Expression.Parameter(typeof(TModel));
// Create lambda
var lambda = Expression.Lambda<Func<TModel, dynamic>>(exp, parameter);
// Try and compile against the view model
var obj = lambda.Compile()(html.ViewData.Model);
like image 299
Gabe Avatar asked Mar 19 '12 20:03

Gabe


2 Answers

The following should work:

public static IHtmlString RenderList<TModel, TItem>(
    this HtmlHelper<TModel> html, 
    Expression<Func<TModel, IEnumerable<TItem>>> dataExpression
)
{
    var parentEx = ((MemberExpression)dataExpression.Body).Expression;
    var lambda = Expression.Lambda<Func<TModel, object>>(parentEx, dataExpression.Parameters[0]);
    var value = ModelMetadata.FromLambdaExpression(lambda, html.ViewData).Model;

    ...
}

Obviously you should add a minimum of error checking to this code which I have intentionally omitted here for brevity.

like image 144
Darin Dimitrov Avatar answered Sep 21 '22 16:09

Darin Dimitrov


You have to use the same parameter expression in your new lambda expression:

var lambda = Expression.Lambda<Func<MyModel, dynamic>>(exp,
    this._dataExpression.Parameters);
like image 38
Balazs Tihanyi Avatar answered Sep 23 '22 16:09

Balazs Tihanyi