Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
1 of 29
http://www.codemag.com/article/1405071
By Leonardo Esposito
Leonardo Esposito
Dino Esposito is the author of
Programming ASP.NET MVC for
Microsoft Press as well as
Programming ASP.NET 4 and
other bestselling books such as
Microsoft .NET: Architecting
Applications for the Enterprise.
03/01/2016 21:56
2 of 29
http://www.codemag.com/article/1405071
DevProConnections
Magazine,
on
the
and
is
mobile
actively
When you open up Visual Studio 2013 with the intent of building a new ASP.NET MVC 5
project, you find only one option: an ASP.NET Web Application. This is great, as it
represents a moment of clarity in a whirlpool of similar-looking and confusing options.
So you click and are presented with various options as to the type of project to create.
You want ASP.NET MVC, right? So you pick up the MVC option. What you obtain is a
no-op demo application that was probably meant to give you an idea of what it means
to code for ASP.NET MVC. Even though the result does nothing, the resulting project is
03/01/2016 21:56
3 of 29
http://www.codemag.com/article/1405071
Youll also find several Nuget packages and assemblies referenced that are not required
by the sample application, yet are already there to save time for when you need them in
action. This is not a bad idea in theory, as nearly any ASP.NET website ends up using
jQuery, Bootstrap, Modernizr, Web Optimization, and others. And if you dont like it, you
still have the option of starting with an empty project and adding MVC scaffolding. This
is better, as it delivers a more nimble project even though there are many references
that are useless at first. The truth is that any expert developer has his own favorite initial
layout of the startup project, including must-have packages and scripts.
Although I may be tempted, I dont want to push my own ideal project layout on you.
My purpose, instead, is applying the Occams Razor to the ASP.NET MVC project
templates that you get in Visual Studio 2013. Ill start with the organization of project
folders and proceed through startup code, bundling, HTML layout, controllers, layers,
HTTP endpoints, and multi-device views. Overall, here are ten good practices for sane
ASP.NET MVC 5 development.
03/01/2016 21:56
4 of 29
http://www.codemag.com/article/1405071
Figure 1 : Unnecessary references in the ASP.NET MVC project built with the default
Visual Studio 2013 template
Its even more interesting to look at the remaining references. Figure 2 shows what you
really need to have referenced in order to run a nearly dummy ASP.NET MVC
application.
03/01/2016 21:56
5 of 29
http://www.codemag.com/article/1405071
03/01/2016 21:56
6 of 29
http://www.codemag.com/article/1405071
either but Ill return to that in a moment.) I sometimes rename Models to ViewModels
and give it a structure similar to Views: one subfolder per controller. In really complex
sites, I also do something even more sophisticated. The Models folder remains as is,
except that two subfolders are addedLInput and View, as shown in Figure 3.
03/01/2016 21:56
7 of 29
http://www.codemag.com/article/1405071
#2 Initial Configuration
A lot of Web applications need some initialization code that runs upon startup. This
code is usually invoked explicitly from the Application_Start event handler. An
interesting convention introduced with ASP.NET MVC 4 is the use of xxxConfig classes.
Heres an example that configures MVC routes:
public class RouteConfig
{
public static void RegisterRoutes(
RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new {
controller = "Home",
action = "Index",
id = UrlParameter.Optional
});
}
}
For consistency, you can use the same pattern to add your own classes that take care of
application-specific initialization tasks. More often than not, initialization tasks populate
some ASP.NET MVC internal dictionaries, such as the RouteTable.Routes dictionary of
the last snippet (just after the heading for #2).For testability purposes, I highly
recommend that xxxConfig methods are publicly callable methods that get system
collections injected. As an example, heres how you can arrange unit tests on MVC
routes.
[TestMethod]
public void Test_If_Given_Route_Works()
{
// Arrange
var routes = new RouteCollection();
03/01/2016 21:56
8 of 29
http://www.codemag.com/article/1405071
Note that the code snippet doesnt include the full details of the code for custom
GetRouteDataFor method. Anyway, the method uses a mocking framework to mock
HttpContextBase and then invokes method GetRouteData on RouteCollection
passing the mocked context.
var routeData = routes.GetRouteData(httpContextMock);
Many developers just dont like the underscore convention in the name of some
ASP.NET folders, particularly the App_Start folder. Is it safe to rename this folder to
something like Config? The answer is: its generally safe but it actually depends on what
you do in the project.
The possible sore point is the use of the WebActivator Nuget package in the project,
either direct or through packages that have dependencies on it. WebActivator is a
package specifically created to let other Nuget packages easily add startup and
shutdown code to a Web application without making any direct changes to global.asax.
WebActivator was created only for the purposes of making Nuget packages seamlessly
extend existing Web applications. As WebActivator relies on an App_Start folder,
renaming it may cause you some headaches if you extensively add/refresh Nuget
packages that depend on WebActivator. Except for this, there are no problems in
renaming App_Start to whatever you like most.
03/01/2016 21:56
9 of 29
http://www.codemag.com/article/1405071
been added mostly for readability purposes, including long descriptive member names.
Although bundling and minification can be applied together, they remain independent
processes. On a production site, theres usually no reason not to bundle minified CSS
and script files. The only exception is for large and very common resources that might
be served through a Content Delivery Network (CDN). The jQuery library is a great
example.
Bundling requires the Microsoft ASP.NET Web Optimization Framework available as a
Nuget package. Downloading the Optimization Framework also adds a few more
references to the project. In particular, they are WebGrease and Microsoft Infrastructure.
These, in turn, bring their own dependencies for the final graph, shown in Figure 4.
03/01/2016 21:56
10 of 29
http://www.codemag.com/article/1405071
within the project where the source code to bundle is located. The argument passed to
the StyleBundle class constructor instead is the public name of the bundle and the URL
through which it will be retrieved from pages. There are quite a few ways to indicate the
CSS files to bundle. In addition to listing them explicitly, you can use a wildcard
expression:
bundles.Add(new Bundle("~/css")
.Include("~/content/styles/*.css");
Once CSS bundles are defined invoking them is as easy as using the Styles object:
@Styles.Render("~/Bundles/Css")
As you can figure from the last two snippets, ASP.NET optimization extensions come
with two flavors of bundle classes: the Bundle class and the StyleBundle class. The
former only does bundling; the latter does both bundling and minification. Minification
occurs through the services of an additional class. The default CSS minifier class is
CssMinify and it is based on some logic packaged in WebGrease. Switching to a different
minifier is easy too. All you do is using a different constructor on the StyleBundle class.
You use the constructor with two arguments, the second of which is your own
implementation of IBundleTransform.
03/01/2016 21:56
11 of 29
Like
StyleBundle,
ScriptBundle
http://www.codemag.com/article/1405071
also
features
constructor
that
accepts
an
03/01/2016 21:56
12 of 29
http://www.codemag.com/article/1405071
document.src = "...";
h.appendChild(script)
Script elements are appended to the HEAD element so that parallel download begins as
soon as possible. Note that this is the approach that most social Web sites and Google
Analytics use internally. The net effect is that all dynamically created elements are
processed on different JavaScript threads. This approach is also employed by some
popular JavaScript loader frameworks these days.
The DOCTYPE instructs older browsers that dont support specific parts of HTML5 to
behave well and correctly interpret the common parts of HTML while ignoring the
newest parts. Also, you might want to declare the character set in the HEAD block.
<meta charset="UTF-8">
The charset attribute is case-insensitive, meaning that you can indicate the character set
name as UTF-8, utf-8 or otherwise. In general, you dont use other character sets than
03/01/2016 21:56
13 of 29
http://www.codemag.com/article/1405071
character set. For example, you can type it in the <globalization> section of the
web.config file. Having it right in each page just adds clarity. An interesting consequence
of clarifying the character set being used is that you can avoid using HTML entities and
type special characters directly in the source. Canonical examples are the copyright
(©), trademark (®), euro (&euro), dashes (—), and more. The only HTML
entities you should use are those that provide the text version of reserved markup
characters, such as less-than, greater-than, and ampersand.
Another rather important meta-tag youll want to have is the viewport meta-tag whose
usage dates back to the early days of smartphones. Most mobile browsers can be
assumed to have a rendering area thats much larger than the physical width of the
device. This virtual rendering area is just called the "viewport." The real size of the
internal viewport is browser-specific. However, for most smart phones, its around 900
pixels. Having such a large viewport allows browsers to host nearly any Web page,
leaving users free to pan and zoom to view content, as illustrated in Figure 5.
03/01/2016 21:56
14 of 29
http://www.codemag.com/article/1405071
initial-scale=1.0,
maximum-scale=1.0,
user-scalable=no" />
In this example, you tell the browser to define a viewport that is the same width as the
actual device. Furthermore, you specify that the page isnt initially zoomed and worse,
users cant zoom in. Setting the width property to the devices width is fairly common,
but you can also indicate an explicit number of pixels.
In ASP.NET MVC, pay a lot of attention to keeping the layout file as thin as possible. This
means that you should avoid referencing from the layout file CSS and script files that are
referenced by all pages based on the layout. As developers, we certainly find it easier
and quicker to reference resources used by most pages right from the layout file. But
that only produces extra traffic and extra latency. Taken individually, these extra delays
arent significant, except that they sum up and may add one or two extra seconds for the
page to show and be usable.
In ASP.NET MVC, a layout page consists of sections. A section is an area that derived
pages can override. You might want to use this feature to let each page specify CSS and
script (and of course markup) that needs be specific. Each layout must contain at least
the section for the body.
<div class="container">
@RenderBody()
<hr />
<footer>
@DateTime.Now.Year - ASP.NET QuickUp
</footer>
</div>
The markup above indicates that the entire body of the page replaces @RenderBody.
You can define custom sections in a layout file using the following line:
@RenderSection("CustomScripts")
The name of the section is unique but arbitrary and you can have as many sections as
you need with no significant impact on the rendering performance. You just place a
@RenderSection call where you want the derived page to inject ad hoc content. The
example above indicates a section where you expect the page to insert custom script
03/01/2016 21:56
15 of 29
http://www.codemag.com/article/1405071
In this case, overridden sections are expected to contain data that fits in the surrounding
markup; otherwise, a parsing error will be raised. In a derived page, you override a
section like this:
@section CustomScripts
{
alert("Hello");
}
03/01/2016 21:56
16 of 29
http://www.codemag.com/article/1405071
breadcrumbs, tabs, accordions, rich buttons, composed input fields, badges and
bubbles, lists, glyphs, and more advanced things, such as responsive images and media,
auto-completion, and modal dialogs. Its all there and definable through contracted
pieces of HTML and CSS classes. Put another way, when you choose Bootstrap, you
choose a higher level markup language than HTML. Its much the same as when you use
jQuery and call it JavaScript. The jQuery library is made of JavaScript but extensively
using it raises the abstraction level of JavaScript.
By the same token, using Bootstrap extensively raises the abstraction level of the
resulting HTML and makes it look like youre writing Bootstrap pages instead of HTML
pages. This is just great for developer-centric Web solutions. Its not good for Web
designers and for more sophisticated projects where Web designers are deeply involved.
From a Web designers perspective, Twitter Bootstrap is just a Twitter solution and even
theming it differently is perceived like work that takes little creativity. From a pure Web
design perspective, Bootstrap violates accepted (best) practices. In particular, Bootstrap
overrides the HTML semantic and subsequently, presentation is no longer separate from
the content. Not surprisingly, when you change perspective, the same feature may turn
from being a major strength to being a major weakness. Just because Bootstrap
overrides the HTML semantic, it tends to favor an all-or-nothing approach. This may be
problematic for a Web design team that joins an ongoing project where Bootstrap is
being used. In a nutshell, Bootstrap is an architectural decisionand one thats hard to
change on the go. So, yes, it makes presentation tightly bound to content. Whether this
is really an issue for you cant be determined from the outside of the project.
Last but not least, the size of Twitter Bootstrap is an issue. Minified, it counts for about
100K of CSS, 29K of JavaScript plus fonts. You can cut this short by picking exactly the
items you need. The size is not an issue for sites aimed at powerful devices such as a PC,
but Bootstrap for sites aimed at mobile devices may be a bit too much. If youre going
to treat desktop devices differently from mobile devices, you might want to look into the
mobile-only version of Bootstrap that you find at .
03/01/2016 21:56
17 of 29
http://www.codemag.com/article/1405071
03/01/2016 21:56
18 of 29
http://www.codemag.com/article/1405071
03/01/2016 21:56
19 of 29
http://www.codemag.com/article/1405071
Figure 7 : The new project infrastructure for worker services and view models
03/01/2016 21:56
20 of 29
http://www.codemag.com/article/1405071
The type T identifies the account class to be managed. The IUser interface contains a
very minimal definition of the user, limited to ID and name. The ASP.NET Identity API
provides the predefined IdentityUser type that implements the IUser interface and adds
a few extra properties such as PasswordHash and Roles. In custom applications, you
typically derive your own user type inheriting from IdentityUser. Its important to notice
that getting a new class is not required; you can happily work with native IdentityUser if
you find its structure appropriate.
User data storage happens via the UserStore<T> class. The user store class implements
the IUserStore interface that summarizes the actions allowed on the user store:
public interface IUserStore<T> : where T:IUser
{
Task CreateAsync(T user);
Task DeleteAsync(T user);
Task<T> FindByIdAsync(string userId);
Task<T> FindByNameAsync(string userName);
Task UpdateAsync(T user);
}
As you can see, the user store interface looks a lot like a canonical repository interface,
much like those you might build around a data access layer. The entire infrastructure is
glued together in the account controller class. The skeleton of an ASP.NET MVC account
controller class that is fully based on the ASP.NET Identity API is shown in Listing 2.
03/01/2016 21:56
21 of 29
http://www.codemag.com/article/1405071
user store and the data store is established in the ApplicationDbContext class. Youll find
this class defined by the ASP.NET MVC 5 wizard if you enable authentication in the
Visual Studio 2013 project template.
public class ApplicationDbContext :
IdentityDbContext<IdentityUser>
{
public ApplicationDbContext() :
base("DefaultConnection")
{
}
}
03/01/2016 21:56
22 of 29
http://www.codemag.com/article/1405071
you can simply use a separate ASP.NET MVC controller and make it return JSON.
There are many posts out there calling for a logical difference between Web API
controllers and ASP.NET MVC controllers. Theres no doubt that a difference exists
because overall Web API and ASP.NET MVC have different purposes. Anyway, the
difference becomes quite thin and transparent when you consider it from the
perspective of an ASP.NET MVC application.
With plain ASP.NET MVC, you can easily build an HTTP faade without learning new
things. In ASP.NET MVC, the same controller class can serve JSON data or an HTML view.
However, you can easily keep controllers that return HTML separate from controllers
that only return data. A common practice consists in having an ApiController class in the
project that exposes all endpoints expected to return data. In Web API, you have a
system-provided ApiController class at the top of the hierarchy for controllers. From a
practical perspective, the difference between ASP.NET MVC controllers and Web API
controllers hosted within the same ASP.NET MVC is nearly non-existent. At the same
time, as a developer, its essential that you reason about having some HTTP endpoints
exposed in some way.
03/01/2016 21:56
23 of 29
http://www.codemag.com/article/1405071
however, is associated with server-side device detection and view routing. By default,
starting with ASP.NET MVC 4, any Razor view can be associated with a mobile-specific
view. The default controller action invoker automatically picks up the mobile-specific
view if the user agent of the current request is recognized as the user agent of a mobile
device. This means that if you have a pair of Razor views such as index.cshtml and
index.mobile.cshtml, the latter will be automatically selected and displayed in lieu of the
former if the requesting device is a mobile device. This behavior occurs out of the box
and leverages display modes. Display modes can be customized to a large extent. Heres
an example:
var tablet = new DefaultDisplayMode("tablet")
{
ContextCondition = (c => IsTablet(c.Request))
};
var desktop = new DefaultDisplayMode("desktop")
{
ContextCondition = (c => return true)
};
displayModes.Clear();
displayModes.Add(tablet);
displayModes.Add(desktop);
The preceding code goes in the Application_Start event of global.asax and clears default
existing display modes and then adds a couple of user-defined modes. A display mode
is associated with a suffix and a context condition. Display modes are evaluated in the
order in which theyre added until a match is found. If a match is foundthat is, if the
context condition holds truethen the suffix is used to complete the name of the view
selected. For example, if the user agent identifies a tablet, then index.cshtml becomes
index.tablet.cshtml. If no such Razor file exists, the view engine falls back to index.cshtml.
Display modes are an extremely powerful rendering mechanism but all this power fades
without a strong mechanism to do good device detection on the server side. ASP.NET
lacks such a mechanism. ASP.NET barely contains a method in the folds of the
HttpRequest object to detect whether a given device is mobile or not. The method is not
known to be reliable and work with just any old device out there. It lacks the ability to
distinguish between smartphones, tablets, Google glasses, smart TVs, and legacy cell
phones. Whether it works in your case is up to you.
If youre looking for a really reliable device detection mechanism, I recommend WURFL,
which comes through a handy Nuget package. For more information on WURFL, you can
03/01/2016 21:56
24 of 29
http://www.codemag.com/article/1405071
Summary
ASP.NET MVC 5 is the latest version of Microsofts popular flavor of the ASP.NET
platform. It doesnt come with a full bag of new goodies for developers but it remains a
powerful platform for Web applications. ASP.NET is continuously catching up with trends
and developments in the Web space and writing a successful ASP.NET MVC application
is a moving target. This article presented ten common practices to build ASP.NET MVC
applications with comfort and ease.
Domain Model
Unless you are creating a plain
CRUD application, the model of
business data (also referred to as
the domain model) is referenced
from a separate assembly and is
created
and
mapped
to
persistence using any of the
known
approaches
in
Entity
Bundling,
03/01/2016 21:56
25 of 29
http://www.codemag.com/article/1405071
Debug Mode
Bundling and minification are not
a functional feature and are,
rather, a form of optimization. For
this reason, it makes no sense for
you to enable both while in
debug
mode.
For
common
default,
bundling
and
debug=false
in
the
check
bundling
and
Ratchet 2.0
Recently, the project codenamed
Ratchet reached version 2.0 and
merged and synced up with
Bootstrap.
considered
Ratchet
the
can
be
mobile-only
and
offers
the
same
03/01/2016 21:56
26 of 29
http://www.codemag.com/article/1405071
03/01/2016 21:56
27 of 29
http://www.codemag.com/article/1405071
Folder
name
Intended goal
App_Data
Contains data used by the application, such as proprietary files (e.g., XML files) or
local databases
App_Start
Contains initialization code. By default, it simply stores static classes invoked from
within global.asax.
Controllers
Models
Views
Folder conventionally expected to group all Razor views used by the application.
The folder is articulated in subfolders,one per controller
03/01/2016 21:56
28 of 29
http://www.codemag.com/article/1405071
Add a comment...
Josh McKinney
On the Config classes tip (#2), I wonder if instead of method params, taking
constructor params and just having a Configure() or Start() method would be
nicer and more DI friendly than writing actual code to call the config code.
Re: tip #6 (don't use bootstrap), I wonder if this is argument is still relevant if
you use the SASS / LESS versions of bootstrap.
Like Reply
John Sully
I recommend version 2.0 which came out in March 2014.
Like Reply May 19, 2014 10:01am
03/01/2016 21:56
29 of 29
http://www.codemag.com/article/1405071
03/01/2016 21:56