4

I am using asp.mvc 4. Assumend I have a Model called Person with the fields

public class Person
{
    public int ID { get; set; }
    public string FirstName { get; set; }
    public string SecondName { get; set; }
    public DateTime DateOfBirth { get; set; }
    public DateTime DateOfWorkstart { get; set; }
    public int NumberOfChildren { get; set; }
    public int DepartmentID { get; set; }
    public virtual Department Department { get; set; }
}

public class Department
{
    public int ID { get; set; }
    public int NameOfDepartment { get; set; }
}

In my automatically generated razor-edit-view fields are shown like this (For clearness, I only included important lines in this post)

@Html.DisplayFor(modelItem => item.FirstName)
@Html.DisplayFor(modelItem => item.SecondName)

now I would like to store the linq-lambda expressions in a list an use it later, I don't know how to do that, I need something like this:

@{
    string itemsToShow = "namepart"; // this could also be "otherpart"
    List <Expression<>> list = new List();
    if (itemsToShow.equals("namepart")
    {
        list.add(modelItem => item.FirstName);
        list.add(modelItem => item.SecondName);
    }
    else
    {
        list.add(modelItem => item.DateOfBirth);
        list.add(modelItem => item.DateOfWorkStart);
        list.add(modelItem => item.NumberOfChildren);
    }
}

and finally I would like to use the generated list like this

@foreach (var lambda in list)
{
    @Html.DisplayFor(lambda)
}

4 Answers 4

1

I'd write a custom helper for this:

public static class HtmlExtensions
{
    public static IHtmlString MyHelper(this HtmlHelper<MyViewModel> html, string itemsToShow)
    {
        var sb = new StringBuilder();
        if (itemsToShow == "namepart")
        {
            sb.Append(html.DisplayFor(x => x.FirstName));
            sb.Append(html.DisplayFor(x => x.SecondName));
        }
        else
        {
            sb.Append(html.DisplayFor(x => x.DateOfBirth));
            sb.Append(html.DisplayFor(x => x.DateOfWorkStart));
            sb.Append(html.DisplayFor(x => x.NumberOfChildren));
        }
        return new HtmlString(sb.ToString());
    }
}

and then inside the view simply:

@Html.MyHelper("namepart")

and if you want to render the other part:

@Html.MyHelper("otherpart")

As an alternative simply put this content into 2 different partial views and then:

@if (itemsToShow == "namepart")
{
    @Html.Partial("_NamePart", Model)
}
else
{
    @Html.Partial("_OtherPart", Model)
}
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for this advice. Yes that seems nice, but my main-goal is to understand how to store lambda-expressions to lists and use it later;
You cannot store it in lists because you do not know the exact type of the lambda expression. You could have used Expression<Func<MyViewModel, object>> but this won't work with the ASP.NET MVC helpers such as DisplayFor if you attempt to use it with a value type property on your model (such as DateTime or integer)
1

There is small progress in my brain. Thanks for your advices. As @darin-dimitrov said, the secret is to store an Expression-Tree. I updated my first post and added a related table. This example works only, when the model has fetched "1 single row from database-table" for example in an edit view;

// first a small helper, which creates the member and checks nullable fields
public static Expression getExpressionPart(ParameterExpression param,
  String s1, String s2)
{
    Expression member = null;
    if (s2 == null)
    {
        member = Expression.Property(param, s1);
    }
    else
    {
        // the second string is to deal with foreign keys/related data
        member = Expression.PropertyOrField(
          Expression.PropertyOrField(param, s1), s2
        );
    }

    Type typeIfNullable = Nullable.GetUnderlyingType(member.Type);
    if (typeIfNullable != null)
    {
        member = Expression.Call(member, "GetValueOrDefault", Type.EmptyTypes);
    }
}

now create the list and the expressions

List<Expression<Func<Person, object>>> list =
  new List<Expression<Func<Person, object>>>();
ParameterExpression param = Expression.Parameter(typeof(Person), "p");

// maps to expression p => p.FirstName
Expression member = getExpressionPart(param, "Firstname", null);
list.Add(Expression.Lambda<Func<Person, object>>(member, param));

// maps to expression p => p.Department.NameOfDepartment
member = getExpressionPart(param, "Department", "NameOfDepartment");
list.Add(Expression.Lambda<Func<Person, object>>(member, param));

and now it works!

@foreach (var lambda in list)
{
    @Html.DisplayNameFor(lambda)
    @Html.DisplayFor(lambda)
}

Comments

0

Have you tried to store the lambda like this:

Func<Person,bool> personExpression = (u => u.FirstName == firstname);

@Html.DisplayFor(personExpression)

And for 2 input types your code would look something like this:

Func<Person,Ticket,bool> personExpression =
        ((u,t) => u.FirstName == firstname && u.SecondName == t.SecondName);

2 Comments

Thanks, but this doesn't work. this results in compiling errors. Did you have success with this approach?
Yeah, it works fine in Console-Solutions. I think Razor didn't support it, or?
0

To loop through model properties in a razor view you should use ViewData.ModelMetadata.Properties as in this answer. For example:

@* Loop through properties. *@
@foreach (var property in ViewData.ModelMetadata.Properties)
{
    @Html.Display(property.PropertyName)
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.