We are currently developing a task management system, a complex front-end project, so let's start with MVC.
WebForm
With the rise of ASP. WebForm has become a yellow flower yesterday, but I really want to say a few words for WebForm.
No experience from ASP to ASP to the students, it is difficult to understand when the WebForm appeared, the program of the ape World cheered. In fact, I was in the razor after the emergence of barely to MVC, because see <%%> this thing is afraid. I have participated in an upgrade ASP to ASP. asp inside the code to see my eyes sour and swollen red tears, all my life remember!
WebForm the last generated HTML may be bloated and unsightly, but its code page (. aspx) is quite refreshing and beautiful.
Now that we have decided to adopt MVC, the WebForm is not enough to say. But we should study hard and learn from its excellent places, which are also used in the development of MVC:
- The rendering is separated from the page logic. WebForm because its framework has explicitly distinguished between ASPX and Aspx.cs, most of the time we don't worry about this. But in MVC, it's easy to use the ViewModel data in the view to operate, blurring the logical boundaries between the controller and the view. We will explain this question in detail at the time of CurrentUser.
- Good page encapsulation and reuse. When we find that the page is recurring, the same "parts", we will certainly think of reuse. This is the time to test our skill. Let me mention one thing I thought: sometimes we would rather repeat than reuse! This is the lesson of my tears. Should be in the Startup Home Project review page section, I have tried to reuse all the comments Partialview, the result is miserable, finally give up reuse, instead of the brighter. In fact, there is a better example is the WebForm in the GridView and Repeater, in practice, but it is a simple package repeater more popular, "chatty" of the GridView but few people use. So encapsulation and reuse have a degree of problem.
Routetest
The route feature is a major breakthrough in MVC and an important flaw. Because there is no good automatic check mechanism, in the actual development process, very error-prone! Believe that there have been experience in the development of students have experience, sometimes long time are error: Can not find the view action, check to find out a spelling error; sometimes add a routeconfig, a while other colleagues called up, "Test!" It turns out that your settings put my cover up. Check me out all afternoon! ”
It is a pity to waste time in these places, so our solution to this problem is to use unit tests, and introduce routetest in Pctest project. Every time new routeconfig, run the unit test: Their own can pass, also do not affect others, OK.
This is the most successful example of unit testing in the UI layer of our project . Supposedly, one of the biggest benefits of MVC is "testable", where unit testing should be widely introduced elsewhere, but I'm lazy, and HttpContext's sealed limit also limits the implementation of unit testing (MVC 5 should solve this problem), So currently the unit tests for the UI layer are not yet expanded. But it is estimated that the work will have to be done sooner or later, and there are some problems with manual testing that are cumbersome and easy to miss.
Url/view level
Another problem with MVC now is that view is difficult to organize at multiple levels. For example, the view I might need is organized like this:
Note that the controller also has a hierarchical relationship setting. I always thought it would be clearer and neater, but if the MVC framework could not be "hierarchical". If you must organize the view hierarchy in this way, you must write the full path to the view in action, such as:
Public class Logcontroller:controller { // // GET:/account/log/on Public ActionResult on () { return View ("~/views/account/log/on.cshtml"); } }
Also have to be specially configured Routeconfig, this also is too troublesome a bit. So, we still try to press the MVC framework, starting with the design of the URL, as far as possible is the/{controller}/{action}/{route-parameter} style, view also, placed in the Contoller corresponding folder can be.
Partial/childaction/editortemplate
When we need to reuse some "page fragments", we are faced with these choices. There are a lot of points to cut into, and we'll just combine our projects and extract their most distinctive and recognizable features, and tell them about their usage scenarios directly:
The first is editortemplate. It has the most obvious characteristics, and is related to post. That is, when a "page fragment" of the data, also need to post back to the server, we must use Editortemplate, if you do not use Editortemplate, ViewModel data can not be passed back (reference: Task management System code in/views/task/editortemplates). Why is it? With the ViewModel binding mechanism of MVC, the HTML control in Editortemplate is rendered with the prefix of the parent model on its name so that MVC automatically parses the post data and binds to ViewModel.
If the "page fragment" does not need post, is only responsible for rendering, but also how to choose? Our principles are:
- If the "page fragment" does not need to interact with the server side, the required data can be obtained from the parent model, using partial;
- Otherwise, if the "page fragment" says that the required data needs to be obtained from the server, then only childaction can be used.
HtmlHelper
In addition to the reuse of several of these page fragments, there is a way to customize the rendering of a "page fragment" by creating a htmlhelper extension method. This approach is generally an alternative to partialview, and we typically use a "very small" (such as a link, a drop-down list, etc.) to encapsulate reusable HTML fragments with a "lot" (or even cross-project) in HtmlHelper. Refer to:
- Documentlink in a task management System Project: Encapsulates an HTML link that always uses the doc.zyfei.net domain name
- Enumdropdownlistfor in the global project ( source code not yet uploaded ): Encapsulates a DropDownList, which is populated with an enum, using the [Description] on the enum. As rendered text
Ajax
Looking at our action, we can see that the action we provide for Ajax is always the actionresult of the return, rather than using a "more advanced" WEBAPI mechanism that returns simple types such as int directly. This is mainly because we are using the sessionperrequest mechanism (mainly to improve performance), we have a request requests only one session (can be understood as a database connection), namely:
- When MVC obtains a request and needs to use the session, the service generates a session;
- Then, throughout the request process, the generated session will be used (similar to "singleton mode");
- When the request is complete, release the session and synchronize all changes to the database
Well, here's our key point is, when is the "Request End"? We further defined it as the view rendering complete, so we took advantage of the filter mechanism to synchronize the database at onresultexecuted(), with the following code:
Public class Sessionperrequest:actionfilterattribute { publicoverridevoid onresultexecuted (resultexecutedcontext Filtercontext) { #if PROD FFLTask.SRV.ProdService.BaseService.EndSession (); #endif base. Onresultexecuted (Filtercontext); } }
So, even with Ajax calls, you have to go through a "view rendering complete" process to complete data synchronization.
Uidevservice switching
For the foreground development, do not need to connect the background database classmate, only need to compile in the MVC project, input Uidev can (if want to really connect the database, use prod), as follows:
So, how is this actually achieved?
In general, we borrowed the AUTOFAC class library to implement what we call "dependency inversion".
So, in the MVC controller, we only use serviceinterface regardless of its implementation, as follows:
Private iauthroizationservice _authservice; Public Authcontroller (iauthroizationservice authservice) { = authservice; }
Finally, in Global.asax.cs we use conditional compiler if...else to determine exactly which service implementation is used:prodservicemodule, or Uidevservicemodule
voidresolvedependency () {varBuilder =NewContainerbuilder (); Builder. Registercontrollers (assembly.getexecutingassembly ()); Builder. Registerfilterprovider ();#ifPRODBuilder. Registermodule (Newprodservicemodule ());#endif#ifUidevBuilder. Registermodule (Newuidevservicemodule ()); #endifContainer=Builder. Build (); Dependencyresolver.setresolver (NewAutofacdependencyresolver (container)); }
Finally, don't forget to add in ProdServiceModule.cs or UIDevServicemodule.cs When a new service is introduced:
Builder. Registertype<registerservice> (). As<iregisterservice> ();
This chapter is about the same. In the next chapter we'll talk about CurrentUser, which leads us to the principle of dividing logic (or responsibility) between view, Controller, service, and ViewModel.
Project Management (vii) MVC