📜 ⬆️ ⬇️

ASP.NET MVC in large projects. Introduction: Model Binding

Instead of intro


While this article was being prepared, a wonderful post 1andy was published. We are organizing view models in ASP.NET MVC , in which much of what I would like to tell the reader is discussed. For this reason, I decided to omit a large introductory introduction and go directly to practical advice.

However, to begin with, you still have to suffer a little with theory.

First, let's define some terms:


Secondly, some introductory:
  1. In our project, we will not simplify our lives with all sorts of AutoMappers and ORMs.
    ')
    In general, it is a good practice if you do something critical in speed. Since I work on projects where the execution time of the request (both HTTP and SQL) is critical, the maximum that I can afford in most cases is the mapping of entities from the database to objects with the most simplified rules. To do this, I use my own wrapper over Dapper.NET.
  2. According to TK, we have a large number of modules with non-intersecting functionality. Each controller, each component of the application is very different from the rest. In practice, this means that the time gained when using copy-paste and code generation does not exceed the time spent on refining the results of these actions.
  3. Our views will contain a fairly large amount of logic. Most of it will undoubtedly be removed from the template in the presenter, but, unfortunately, it will not be possible to get rid of it completely.
  4. The application has quite strict requirements for browser support. This means that we have to minimize the use of CSS selectors and use "Unobtrusive JavaScript", while maintaining performance when JS is disabled.


Model Data Binding


Alas, before moving forward, we need to understand in general how the data binding mechanism in ASP.NET MVC works and consider some of the problems associated with it.

The HtmlHelper class represents methods for generating HTML markup for your page. Most of them are associated with control tags for forms (input, label, etc.). It is they who interest us.
These methods can be divided into two categories, let's call them "generators by essence" and "generators by expression". For example, take the text field output method (<input type = "text" />).

The entity generator is the Html.TextBox () method. In fact, it has nothing to do with data binding - you fill it in yourself, you specify its current value yourself.

The expression generator, Html.TextBoxFor (), is already much more interesting. The first thing to notice is that instead of passing a specific value in the value field, we will pass a function that returns this value from our model (in this case, it will be the presenter, but more on that later). If you dig a little deeper, then we will see that in fact we are passing not the function itself, but some Expression - a class that describes our function.

What does this give?
Expression allows us to get the metadata of the called function, in particular, to select its component parts. ASP uses this information to build the name and id fields of the generated tag.
Schematically, these fields are as follows:
  1. ViewData of the current view takes the prefix of this view (about it below)
  2. From Expression, a string is generated that repeats the code of the passed function without calling the model.
    For example, from
    Html.TextBoxFor(m => m.SomeArray[someIndexer])
    at someIndexer = 2 we get the string "SomeArray [2]".
    How specifically it works I will not explain, you can see for yourself in the source code. But there is one important point:
    the expression is correctly generated only when the depth of field <3
    Those. expression
    Html.TextBoxFor(m => m.SubModel.SomeArray[someIndexer])
    may not work
  3. The prefix obtained in step 1, if any, is combined with the string obtained in step 2. The result is the value of the name field of the resulting tag.
  4. Further in this value all the characters '.', '[', ']' Are replaced by underscores ('_'). This is the value of the id field.
As I promised, consider where the presentation prefix comes from.
When you generate a view from the controller by calling this.View (), a new object of the ViewDataDictionary class is created. It has a TemplateInfo field that stores the string HtmlFieldPrefix. This is the current view prefix. By default, it is empty.
Further, in ASP.NET MVC, there are two ways to call another from one view — either run it in a completely isolated context (as HtmlHelper.Partial () does), or render it as a “subview” of the current — these are the HtmlHelper.DisplayFor () and HtmlHelper methods .EditorFor ().
The second case is interesting. Calling these methods is very similar to calling the generator by an expression for individual tags, but instead of the resulting tag, we get a presentation rendered with a new template prefix.

Small example
Let there is a view with the MainView.cshtml template, called from the controller:
@model App.Models.MainModel
<h1>MainView.cshtml</h1>

@using (this.Html.BeginForm())
{
this.Html.TextBoxFor(m => m.SomeText)
this.Html.EditorFor(m => m.SubModel, "SubView")
}

And the view with the SubView.cshtml template lying in Views / Shared / EditorTemplates:
@model App.Models.SubModel
<h2>SubView.cshtml</h2>

this.Html.TextBoxFor(m => m.SubText)

As a result of rendering the following tags would turn out:
  1. <input type = "text" id = "SomeText" name = "SomeText" />
  2. <input type = "text" id = "SubModel_SubText" name = "SubModel.SubText" />

As we can see, the SubModel prefix was passed to the SubView representation, corresponding to the expression passed to the HtmlHelper.EditorFor () method.


So, the markup is rendered, the client has entered the data, the request has been sent to the server. Now we will consider how the incoming data is processed and transformed into models (we will consider the case when the model is a user class).
  1. The routing mechanism determines the controller method that is most suitable for processing an incoming request.
  2. The signature of the selected method is analyzed. Parameters that match the name with the transmitted data are populated with incoming data to bypass the binding.
  3. From the remaining parameters, the class of the model that action expects is calculated.
  4. An object of the model class is created by calling the default constructor (with empty parameters).
    Important: the constructor must be exactly default, not containing parameters. Optional parameters will not pass
  5. For each incoming field that does not contain a period ("."), The field of the same name is filled in the created model
  6. Further, for all the remaining fields, the corresponding prefix fields are searched for. They are filled in according to the same principles starting from step 3

Everything is simple and obvious. However, in general, this mechanism needs to be known in order to be able to make its own data binding algorithm for the model (I personally had to do this to create transparent stateful models, I will describe in one of the following parts).


Conclusion

This concludes the theoretical part, we have learned the minimum necessary to start using ASP.NET MVC effectively, and we can begin to practice

Source: https://habr.com/ru/post/119353/


All Articles