ASP.NET MVC Model Binding with C# 6

I usually struggle over ASP.NET MVC Model Binding mechanics. With this blog post I try to bring some light to the topic and show you how you can use C# 6 to strengthen your ASP.NET MVC Model Binding capabilities. I often see View Models like this:

To have a distinction between a model that is used to show something (ViewModel) and a model that is used to post values to a controller (InputModel) is in my opinion a good thing. With this distinction it is easier to recognize what has been sent over the wire, what needs to be validated or what is user data that you should not trust at all. This code is far from optimal, because it violated the principle of “Favor Composition over inheritance”.
So let’s try to use composition:

In a Razor view we can write the following:

And have a Controller Action like this:

It’s kinda tricky that this works out of the box. At first it is important to know that the model binding is case insensitive. Secondly the prefix part of the HtmlHelper expression must match the parameter name of the Action method (case insensitive).

blogBinding

As we can see Command equals case insensitively command. If you rename the parameter to cmd it won’t work anymore. Or if you rename the property Command to DeleteCustomerCommand it won’t work anymore too.

To avoid bugs that can come from such naming or renaming actions, I recommend you to use the BindAttribute with new C#6 keyword nameof.

If you like nicer API’s like I do, you could write your own Attribute.

Now you have an action method like this:

Happy binding!

Developer Open Space 2015

Einladung zum Developer Open Space 2015

Das Besondere am Konzept Open Space ist der freie Tagungscharakter. Bis kurz vor Beginn existiert keine festgelegte Agenda. Die Teilnehmer bestimmen die Themen des jeweiligen Tages gemeinsam. Die konkrete Programmiersprache ist dabei weniger entscheidend. Der Grund für alles ist einfach erklärt: Gute Gespräche hat man häufig ohne Agenda, beim Kaffee und beim „du“.

vyRdM6Z38v2wUjKM5_qCure1d5YR-ejQSHucFu-YkWU=w317-h211-p-no

Vor acht Jahren gab es die erste Ausgabe der (Un-)Konferenz mit dem Namen Developer Open Space, die genau nach diesem Prinzip eines freien Tagungscharakters aufgebaut ist und sie findet dieses Jahr vom 16.–18. Oktober 2015 in Leipzig statt. Natürlich gibt es auch „Klassiker“, die jedes Jahr von den Teilnehmern als Themen festgelegt werden, nämlich vor allem der Erfahrungsaustausch wie „Git richtig einsetzen“, „Scrum ruinieren“, „PowerPoint zerstört Wirkung“, „NuGet ist kaputt“, „Telemetrie in Web-Apps“, „Gescheiterte Projekte – Was gelernt?“ oder „Warum ist die WCF nur so langsam?“. Daneben gibt es auch rein technische Themen wie etwa AngularJS, Docker, HTML5/CSS3 oder NoSQL.

Ergänzt wird der Open Space um einen Workshop-Tag, bei dem die Teilnehmer aus rund 20 Workshops auswählen können, beispielsweise zum Thema Agilität. Die Fotos von der Konferenz sprechen für sich. Alles ist soweit wie möglich selbst organisiert, das heißt, die Räume, die Infrastruktur, das Catering, aber eben auch nicht mehr.

d2EewAf0C6N9aOcNonjdONEMRHlwI-qBRo7I1Rb9NVY=w281-h187-p-no

Anmeldung

Die Anmeldung ist seit einiger Zeit möglich. Die Plätze sind begrenzt. Nimm teil! Informiere gerne auch Kollegen, befreundete Unternehmen oder Bekannte über diese gute Sache.

Type Safe List Model Binding with ASP.NET MVC By Building A Custom MVCContrib GridRenderer

When you try to post a list of data to an ASP.NET MVC Controller you have to know some model binding concepts. Per default the DefaultModelBinder is used. You need to prepare the names of your input fields so that the model binder can bind them to a generic list object.

At first you have to define an hidden input field for each item. The hidden input fields containing the index name and the index value that are used to determine each item in your list. After that all of your text fields need a special naming.

<input type="hidden" name="list.Index" value="0" />
<input type="text" name="list[0].Name" value="Alex" />
<input type="text" name="list[0].Description" value="..." />

<input type="hidden" name="list.Index" value="1" />
<input type="text" name="list[1].Name" value="Chris" />
<input type="text" name="list[1].Description" value="..." />

list is the name of the list parameter in your controller action method where you want to post the data.

[HttpPost]
public ActionResult PostData(List<ItemModel> list)

The 0/1 is the index value. You can also use a Guid.

That’s all 🙂 Now the DefaultModelBinder knows how to bind the data to your generic list.
There is one big drawback. Defining the name for each input field is not type safe right now. So if we for instance have a typo the compiler is not throwing an exception.

A solution is to use ViewData.TemplateInfo.HtmlFieldPrefix.
With the HtmlFieldPrefix you can define a Html field prefix for each input field, so that you can still use the strongly typed Html helpers.


@using (Html.BeginForm("PostData", "Home", FormMethod.Post, new { id = "simpleForm" }))
{
ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix = "list[0]";
<input type="hidden" name="list.Index" value="0" />
@Html.TextBoxFor(x => x.Name)
@Html.TextBoxFor(x => x.Description)

ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix = "list[1]";
<input type="hidden" name="list.Index" value="1" />
@Html.TextBoxFor(x => x.Name)
@Html.TextBoxFor(x => x.Description)

<input type="submit" value="submit" id="btnSubmit" />
}

I used this knowledge to write a custom renderer for the MVCContrib Grid.
You can use the custom grid renderer like this:

@using (Html.BeginForm("PostData", "Home", FormMethod.Post, new { id = "simpleForm" }))
{
@(Html.Grid(Model.Items)
.RenderUsing(new PostAsListRenderer<ItemModel>("list"))
.Columns(c =>
{
c.For(x => Html.Partial("Grid/Id", x)).Named("Id");
c.For(x => Html.Partial("Grid/Name", x)).Named("Name");
c.For(x => Html.Partial("Grid/Description", x)).Named("Description");
c.For(x => Html.Partial("Grid/SelectedItem", new ListModel { SelectedItem = x.SelectedItem, SelectListItems = Model.SelectListItems })).Named("DropDown");
}))

<input type="submit" value="submit" id="btnSubmit" />
}

You may wonder why I use all those partial views. The reason is a bug in the ExpressionHelper of ASP.NET MVC.
Right now you can’t write this:

@using (Html.BeginForm("PostData", "Home", FormMethod.Post))
{
@(Html.Grid(Model.Items)
.RenderUsing(new PostAsListRenderer<ItemModel>("list"))
.Columns(c =>
{
c.Custom(
@<text>
@Html.HiddenFor(x => item.Id)
@item.Id
</text>).Named("Id");
c.For(x => Html.TextBoxFor(y => x.Name)).Named("Name");
c.For(x => Html.TextBoxFor(y => x.Description)).Named("Description");
c.For(x => Html.DropDownListFor(y => x.SelectedItem, Model.SelectListItems)).Named("DropDown");
}))

<input type="submit" value="submit" />
}

As you can see we are using a special lambda expression Html.TextBoxFor(y => x.Name). Those lambda expressions are wrongly parsed by the ExpressionHelper.
I already created a bug report.

Here you can see how I implemented the PostAsListRenderer.

public class PostAsListRenderer<T> : HtmlTableGridRenderer<T> where T : class
{
private readonly string _collectionName;
private string _previousHtmlFieldPrefix;
Guid _currentIndex;
bool _firstCellRendered;

public PostAsListRenderer(string collectionName)
{
_collectionName = collectionName;
}

protected override void RenderStartCell(GridColumn<T> column, GridRowViewData<T> rowData)
{
string attrs = BuildHtmlAttributes(column.Attributes(rowData));

if (attrs.Length > 0)
attrs = " " + attrs;
var hidden = string.Empty;

if (!_firstCellRendered)
{
hidden = string.Format("<input type=\"hidden\" name=\"{0}.index\" value=\"{1}\" />", _collectionName, _currentIndex);
}

_firstCellRendered = true;
RenderText(string.Format("<td{0}>{1}", attrs, hidden));
}

protected override void RenderRowStart(GridRowViewData<T> rowData)
{
_firstCellRendered = false;
_currentIndex = Guid.NewGuid();
Context.ViewData.TemplateInfo.HtmlFieldPrefix = string.Format("{0}[{1}]", _collectionName, _currentIndex);

base.RenderRowStart(rowData);
}

protected override void RenderGridStart()
{
_previousHtmlFieldPrefix = Context.ViewData.TemplateInfo.HtmlFieldPrefix;
base.RenderGridStart();
}

protected override void RenderGridEnd(bool isEmpty)
{
Context.ViewData.TemplateInfo.HtmlFieldPrefix = _previousHtmlFieldPrefix;
base.RenderGridEnd(isEmpty);
}
}

Pay attention that the collection name is the name of your parameter in your post action.

If you have any suggestions or questions, please feel free to contact me.