Sei sulla pagina 1di 33

By Jonathan Danylko for Udemy

Interested in more than just a guide? Check out a full course.


TABLE OF CONTENTS: CLICK TO JUMP TO A SPECIFIC SECTION

In this introduction to Microsofts ASP.NET MVC, we will discuss the


technology, terminology, and techniques for building your very first
ASP.NET MVC application.
While this is considered an introductory course, this makes a few
assumptions. You should have a good understanding of C#, objectoriented concepts, and the way the Internet works in general to fully
understand the workings of an ASP.NET MVC application.
Lets start with the basics of defining ASP.NET MVC.
What is ASP.NET MVC?
ASP.NET MVC is Microsofts framework for developing fast web
applications using their .NET platform with either the C# or VB.NET
language.
MVC is not a new concept and was introduced back in the 1970s using a
language called Smalltalk-76. It was also implemented in the 1980s using
Smalltalk-80.
MVC is an acronym that stands for:
(M)odel Objects that hold your data.
(V)iew Views that are presented to your users, usually HTML
pages or pieces of HTML.
(C)ontroller Controllers are what orchestrates the retrieving and
saving of data, populating models, and receiving data from the
users.
Microsoft has taken these concepts and integrated them into a flexible and
easy web framework, calling it ASP.NET MVC.
For those unaware of ASP.NET, below is a list of terms to understand the
technology well be using in this tutorial.

.NET Framework A technology introduced in 2002 which


includes the ability to create executables, web applications, and
services using C# (pronounced see-sharp), Visual Basic, and F#.
ASP.NET An open-source server-side web application framework
which is a subset of Microsofts .NET framework. Their first
iteration of ASP.NET included a technology called Web Forms.
ASP.NET WebForms (2002 current) A proprietary technique
developed by Microsoft to manage state and form data across
multiple pages.
Why is it so popular?
Since ASP.NET MVC appeared eight years ago, it has been a breath of
fresh air for Microsoft developers. Most developers stopped working with
WebForms to move forward with MVC.
But why has it become so popular?
ASP.NET MVC has a separation of concerns.
Separation of concerns means that your business logic is not
contained in a View or controller. The business logic should be
found in the models of your application. This makes web
development even easier because it allows you to focus on
integrating your business rules into reusable models.
ASP.NET MVC provides testability out of the box.
Another selling point is that ASP.NET MVC allows you to test every
single one of your components, thereby making your code almost
bulletproof. The more unit tests you provide for your application,
the more durable your application will become.
ASP.NET MVC has a smaller View footprint.
With WebForms, there is a server variable called ViewState that
tracks all of the controls on a page. If you have a ton of controls on
your WebForm, the ViewState can grow to become an issue.
ASP.NET MVC doesnt have a ViewState, thus making the View lean
and mean.
ASP.NET MVC has more control over HTML.
Since server-side controls arent used, the View can be as small as
you want it to be. It provides a better granularity and control over
how you want your pages rendered in the browser.

There are a number of other reasons, but a lot of thought was put into
ASP.NET MVC and over eight years, it has matured into an exceptional
framework for web developers.
Requirements and installation
To start building ASP.NET MVC applications, it is recommended that you
have the following:
A version of Visual Studio (preferably 2013, 2015, or Community
Edition) installed on your machine. The Community Edition can be
found athttp://www.visualstudio.com/ .
.NET Framework (4.0 or higher)
Once youve installed Visual Studio, we can create a sample project to find
out whats included with an MVC project.
Layout of an MVC project
I know you want to jump right into development, but you have to
understand where everything is at before you start coding.
You need to know where the hammer and screwdriver are before building,
right?
When you create a new MVC project, your solution should have the
following structure in your Solution Explorer.

App_Data While I dont use this folder often, its meant to hold data
for your application (just as the name says). A couple of examples would
include a portable database (like SQL Server Compact Edition) or any kind
of data files (XML, JSON, etc.). I prefer to use SQL Server.
App_Start The App_Start folder contains the initialization and
configuration of different features of your application.
BundleConfig.cs This contains all of the configuration for
minifying and compressing your JavaScript and CSS files into one
file.
FilterConfig.cs Registers Global Filters.

RouteConfig.cs Configuration of your routes.


There are other xxxxConfig.cs files that are added when you apply other
MVC-related technologies (for example, WebAPI adds WebApiConfig.cs).
Content This folder is meant for all of your static content like images
and style sheets. Its best to create folders for them like images or
styles or css.
Controllers The controllers folder is where we place the controllers.
Models This folder contains your business models. Its better when you
have these models in another project, but for demo purposes, well place
them in here.
Scripts This is where your JavaScript scripts reside.
Views This parent folder contains all of your HTML Views with each
controller name as a folder. Each folder will contain a number of cshtml
files relating to the methods in that folders controller. If we had a URL
that looked like this:
http://www.xyzcompany.com/Products/List

we would have a Products folder with a List.cshtml file. We would also


know to look in the Controllers folder and open the ProductsController.cs
and look for the List method.
Views/Shared The Shared folder is meant for any shared cshtml files
you need across the website.
Global.asax The Global.asax is meant for the initialization of the web
application. If you look inside the Global.asax, youll notice that this is
where the RouteConfig.cs, BundleConfig.cs, and FilterConfig.cs are called
when the application runs.
Web.Config The web.config is where you place configuration settings
for your application. For new MVC developers, its good to know that you
can place settings inside the <appsettings> tag and place connection
strings inside the <connectionstring> tag.
Now that you know where everything is located in your project, we can
move forward with what is the process when an MVC application is
initially called.
General flow of an MVC application

The flow of an MVC application can get confusing, but I want to visually
show you how a simple request moves through the ASP.NET pipeline.
1. A user requests a web page using a browser.
2. The server receives the request.
3. If this is the first time the web application is called, Route objects
are added to the RouteTable object. Once the route table is created,
we build additional routing objects and perform the routing using
the RouteTable, which creates the RouteData, then the
RequestContext.
4. Now that we have our Request Context, the MvcRouteHandler
creates an MvcHandler. The MvcHandler is what kicks off the start
of an MVC Application. It also identifies which controller to use
based on the HTTP request. The MvcHandler creates the controller
and calls the execute method.
5. Once we have our Controller identified and selected, we need to find
the method to execute. This is done by using the
ControllerActionInvoker. Once found, it executes that method.
6. The action method is executed while passing in user parameters,
orchestrates response data, and returns a resulting ActionResult.
Usually, a ViewResult is returned by MVC.
7. The data is passed through to either the default View or a specified
different View through a ViewResult. The data is laid into the View
(HTML) using Razor placeholders. Think of it as a template.
8. Once the data is in the View, it now processes the View, performing
replacements by executing code resulting in plain HTML.
9. The final View is sent back to the user in their browser.

If this seems like a lot to remember, its okay. Youll see how easy the
process is while we work through this tutorial.
Lets start with the basics.

Understanding the essentials


Models
What are Models?
Models are probably the easiest section to address first. Models are the
objects that define and store your data so you can use them throughout
the application.
I would consider Models to be the equivalent of plain old CLR (Common
Language Runtime) objects, or POCOs. A POCO is a plain class that holds
structured data. Thats it.
For example, if you had a business, one simple POCO (or model) would be
similar to:

public class Customer


{
public string FirstName { get; set; }

public string LastName { get; set; }


public string Company { get; set; }
public IEnumerable<Order> Orders { get; set; }
}

Of course, you do have options when adding additional functionality to


your POCOs. For example, how long have they been with you as a
customer?
Your business Model may look something like this:

public class Customer


{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Company { get; set; }
public IEnumerable<Order> Orders { get; set; }
public DateTime FirstMet { get; set; }
public int CalculateAge(DateTime endTime)
{
var age = endTime.Year - FirstMet.Year;
if (endTime < FirstMet.AddYears(age))
age--;
return age;
}
}

While this is a very simple example of what a model is, this wont be the
last time we discuss models.
Controllers
Controllers are considered to be the coordinators or heavy lifters of your
application.
When a request comes in for a web page (GET), it goes through a
controller. If a user hits a submit button (POST), that data is sent back to a
controller. You can even use controllers to return smaller portions of
HTML.
A controller has the following structure:

public class ExampleController : Controller

{
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(ExampleViewModel model)
{
return View();
}
}

IMPORTANT: You may notice that the name of our class is called
ExampleController. This is a convention in MVC. All controllers are
required to have a suffix of controller.
It also makes it easy to spot when building your URLs.
In the example above, our URL would be:
http://www.xyzcompany.com/Example/Index

or
http://www.xyzcompany.com/Example

In WebForm speak-ese, the Index method is the equivalent to the


Default.aspx on each controller. The Index is assumed and automatically
added to the end of the URL, so both URLs end up going to the Index
method.
ActionResults

At the end of each method, youll notice a return

View();.

What is this?

This View method is called an ActionResult. ActionResults are a way to tell


the controller what to return to a View or what to return to the browser.
Essentially, ActionResults are what you want to return when you are done
processing your Action method. Well talk about a number of
ActionResults later in the tutorial.
In this case of the ExampleController above, no data is sent to the Index
view. If you observe a method returning an ActionResult without any data
(like View()), its safe to say that a model isnt attached to the View.

When you see the View() returned, what is actually happening behind the
scenes is that the controller creates a new ViewResult and returns that as
the result. The View method is just a way to camouflage the underpinnings
of what makes a ViewResult function properly.
You could very easily do something like this:
public ActionResult Index()
{
return new ViewResult
{
ViewName = "Index"
};
}

But the developers of the ASP.NET MVC framework have made this
extremely easy to use by creating a View() method at the controller level.
The returning of the View() with the default method simplifies the
process.
Most developers new to MVC have asked the question, Whats the
difference between a ViewResult and an ActionResult?
The answer is that a ViewResult is a type of ActionResult. ViewResult is
inherited from an ActionResult. There are a number of additional
ActionResults that well visit later, but for our purposes right now, the
default behavior to an Action Method should be a ViewResult (which is an
ActionResult).
ActionFilter

If you are familiar with attributes in .NET, youll get a fair amount of them
in ASP.NET MVC.
An ActionFilter is merely an attribute that is attached to a controller or
action method to perform a certain task.
One specific ActionFilter that is used a lot is the Authorize filter. Using our
example above, the ExampleController doesnt have an ActionFilter
attached to the Index page.
When we attach an Authorize ActionFilter to the method, it automatically
requires authorization to access the page. Your authorization could be
forms authentication, Windows, cookie, or whatever type of
authentication you have on your site.

[Authorize]
public ActionResult Index()
{
return View();
}

This means that if someone tries to go to /Example/Index and they


havent been authorized to use that page, they wont be able to go to the
Index page. If you want people to access that web page, remove the
[Authorize] off the method.
ActionFilters are very powerful. There are a number of ActionFilters that
can compress pages before sending them to the server, remove white
space from a page, and even log data after execution.
TryUpdateModel

When we post data back to our POST action method, we need to update
our model with the contents of our ViewModel.
Instead of performing the left=right syntax, there is a method in the
controller called TryUpdateModel. There is also an UpdateModel method
as well, but I consider the TryUpdateModel to be more versatile.
While we havent talked about Views yet, our HTML View can have a form
that looks like this:
@using (Html.BeginForm("Index", "User", FormMethod.Post))
{
@Html.TextBox("FirstName", String.Empty)
<span class="input-group-btn">
<button class="btn btn-default" type="submit">Save</button>
</span>
}

When the Save button is pressed, the User Controller and Index Action
method are called. This is sent back to the POST Index page, not the GET
index page. You can specify in the View form which Postback method you
want to use (either FormMethod.Post or FormMethod.Get).
[HttpPost]
public ActionResult Index(UserViewModel model)
{
var user = new User();
if (TryUpdateModel(user))
{

// Save new user variable to the database.


return Redirect("/User/List");
}
return View(model);
}

This is a loaded section to discuss. There is a lot happening in this Action


method.
First, we have an HttpPost ActionFilter on our method so we can tell MVC
explicitly that this is our POST Index Action method.
The ViewModel is sent to the controller, and we create a new instance of a
user.
Next, the TryUpdateModel takes our new instance of a user and tries to
map the properties in the model with properties in the user object.
Keep in mind that it also takes into account Validation Attributes while
the update is occurring on each field. If a field is required and theres
nothing there, the TryUpdateModel will fail and mark that property with
an error message.
If it succeeds, then it will save to the database and return to the User
Listing page.
If it doesnt succeed, the model will return to the current View (which
contains errors in the model) and display the errors on the page.
Views
Since we just discussed controllers, this is a nice segue into the discussion
about Views. A View is the missing piece for a complete MVC cycle and
how everything connects.
Views are your HTML pages. They are merely templates with
placeholders.
Out of the box, ASP.NET MVC includes two types of ViewEngines:
WebForm and Razor. For this tutorial, well be using the Razor syntax.
Lets get a little ambitious. What if we wanted to send data from a
controller to a View?

There are a number of ways we can accomplish this. They are ViewData,
ViewBag, TempData, and ViewModels.
ViewData

Defined as a dictionary object in the controller class, the ViewData assigns


a key/value pair to pass the data over to the View.
Since this is part of the controller class, we do not need to explicitly pass it
to the View. It will automatically send it over.
For example, in our controller above, we can create a variable called
message and a variable called customers.
public ActionResult Index()
{
// in our controller code.
var customer = "Jose";
var account = 1;
ViewData["Message"] = string.Format("Customer: {0} ({1})",
customer, account);
var customers = new List { "John", "Bob", "Fred" };
ViewData["Customers"] = customers;
return View();
}

In our View, we can have the following:


<!DOCTYPE html>
<html>
<head>
<title>Index</title>
</head>
<body>
@ViewData["Message"]
<hr />
<ul>
@foreach (var customer in (ViewData["Customers"] as
List<string>))
{
<li>@customer</li>
}
</ul>
</body>
</html>

In the Razor syntax, you need to prefix your code with an at (@) symbol to
display any data from your models.

Quick Tip: If you want to display an at symbol in your View, use two at
symbols (@@).
As you can see, this muddies your Views by casting your ViewData as types
(List<string>). However, this simple technique to access a dictionary
string is not strongly-typed, meaning we dont have a way to suggest the
type of item we want to display in the View.
While this is a good way to pass data, its not ideal.
ViewBag

ViewBag is a little bit better when it comes to passing data. It wraps the
ViewData and makes the object type dynamic so we can immediately find
it in our View.
Here is an example of our controller:
public ActionResult Index()
{
var customer = "Jose";
var account = 1;
ViewBag.Message = string.Format("Customer: {0} ({1})", customer,
account);
var customers = new List { "John", "Bob", "Fred" };
ViewBag.Customers = customers;
return View();
}

And our View would look like this:


<!DOCTYPE html>
<html>
<head>
<title>Index</title>
</head>
<body>
@ViewBag.Message
<hr />
<ul>
@foreach (var customer in ViewBag.Customers)
{
<li>@customer</li>
}
</ul>
</body>
</html>

ViewBag is much the same as ViewData: set it and forget it. It will
automatically be sent over to the View for immediate use.
In our example using ViewBag, we were able to remove our casting of our
object types to make the View cleaner, but we can do better than ViewBag
and ViewData.
TempData

As a side note, there is another type of dictionary in the controller called


TempData. TempData is set once and when the request and response are
complete, it is removed and never used again.
A good use of this would be when a user posts data back to the HttpPost
action method. If everything went well and we didnt have any problems
with saving our data, we would create a TempData[Message] saying
Everything was saved. and redirect to the HttpGet Index Action Method.
In the HttpGet Action Method, we would check to see if there was a
message in the TempData dictionary. If there was, set the
ViewBag.Message to the TempData[Message].
Here is an example of a controller:
public ActionResult Index()
{
var customer = "Jose";
var account = 1;
if (TempData["Message"] != null)
{
ViewBag.Message = TempData["Message"].ToString();
}
else
{
ViewBag.Message = string.Format("Customer: {0} ({1})",
customer, account);
}
return View();
}
[HttpPost]
public ActionResult Index(FormCollection forms)
{
if (forms["name"] != "John")
{
TempData["Message"] = "This data is only for John. Access
Denied.";
}
return RedirectToAction("Index");
}

When a post occurs, a message is set in the TempData dictionary. Did you
notice the RedirectToAction(Index)? This is a type of ActionResult and
will redirect the page over to the HttpGet version of Index.
In the HttpGet Index Action Method, we check to see if there is a
TempData message. If there is, we set the ViewBag.Message to the
TempData message. If there isnt a TempData message, we default the
Message to the customer name and customer number.
We show the message in the View using @ViewBag.Message where you
want the message to appear.
ViewModels

ViewBag, ViewData, and TempData are great for passing simple data to a
View, but what if you have complex data? How do you pass multiple
classes, or models, over to the View?
This last method is my preferred way of passing data from a controller to a
View, and its called ViewModels. ViewModels are nothing more than a
collection of models in one class.
If we were building a car, we would need parts for this car. So we would
have a car frame object, a steering wheel object, and options for the car.
public class CarFrame
{
public string Manufacturer { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public int Year { get; set; }
}
public class SteeringWheel
{
public string Color { get; set; }
public string Type { get; set; }
}
public class CarOptions
{
public bool AirConditioning { get; set; }
public bool CdPlayer { get; set; }
public bool DvdPlayer { get; set; }
public bool PowerSeats { get; set; }
}

If the user wants to see all of the components on one screen, you need to
pass all of those objects over.
Lets create our CarViewModel.
public class CarViewModel
{
public CarFrame CarFrame { get; set; }
public SteeringWheel SteeringWheel { get; set; }
public CarOptions Options { get; set; }
}

As you can see, its a very simple object. Remember that a ViewModel is
strictly a container to hold Models for the View. Its just a container to
transport the data.
Thats all it is.
When you are using ViewModels, there are a couple of things you need to
remember.
ViewModels are different. When you pass a ViewModel from a
controller to a View, you have to pass it through the View() method
at the end of your Action method (i.e., return View(myViewModel); )
In your View, you need to include the model type at the very top.
This explains to the Razor ViewEngine what objects this view will
use. For example, our CarViewModel would look like this in the
View using the Razor syntax:
@model <namespace>.CarViewModel

This step alone provides us with IntelliSense in our Views.


Our controller would look like this:
public ActionResult Index()
{
var carFrame = new CarFrame
{
Year = 2015,
Manufacturer = "Dodge",
Make = "Charger",
Model = "RT"
};

var wheel = new SteeringWheel();


var options = new CarOptions
{
AirConditioning = true,
DvdPlayer = true,
CdPlayer = true,
PowerSeats = false
};
var viewModel = new CarViewModel
{
CarFrame = carFrame,
Options = options,
SteeringWheel = wheel
};
return View(viewModel);
}

The ViewModel is like a package that places everything necessary to


display an entire View of data to the user. Once the ViewModel is built,
we pass it over to the View.
Our View will look like the following:
@model CarViewModel
<!DOCTYPE html>
<html>
<head>
<title>Car Parts</title>
</head>
<body>
<p>Car: @Model.CarFrame.Year @Model.CarFrame.Manufacturer
@Model.CarFrame.Make @Model.CarFrame.Model</p>
</body>
</html>

The ViewModel method of passing data provides a solid way to build your
Views using strong-typed objects instead of magic-strings or dynamic
objects.
As mentioned before, you even receive a bonus of IntelliSense for assisting
you in creating your Views.
Routes

Routing is extremely important in ASP.NET MVC. Its a way to direct each


URL to its proper controller and action.
The routes are stored in the App_Start\RouteConfig.cs file. The Default
route looks like this:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id =
UrlParameter.Optional }
);

Most developers keep this standard and dont modify the routes because
of the complexity it could incur on the application.
If you are satisfied with the way your application routes the URLs with
this basic route, then by all means, you can leave it just the way it is and
move on with building your application.
However, if you need to make new routes, its essential that you
understand how it breaks the URL into segments to know which controller
is used.
If we have the following URL:
http://localhost/Products/List

and we broke these pieces down to their basic routing components, it


would look like this:
Domain (http://localhost/) is not used
Products is the controller name
List is the action name
Id would not be used in this case because we didnt provide it.
If we changed our URL to something like this:
http://localhost/Products/Edit/5

The route would be broken down like this:


Again, domain is not used in the routing.

Products, again, is the name of the controller.


Edit is the name of the Action in the controller.
Id would contain the value 5 and be a parameter passed into the
Edit Action in the controller.
So your controller would look like this:
public class ProductsController : Controller
{
public ActionResult Edit(string id)
{
// id would equal 5
return View();
}
}

Now that youve seen a couple of examples, lets break down the
MapRoute method to understand what it expects.
The name of the route (Default in this case) can be anything you want
(preferably something descriptive so you know what route it is). You could
even have a route called BlueberryPie, but it has to be unique. If you
define another route in your RouteConfig, you cant have another
BlueberryPie route.
The url parameter is the pattern that your URL will accept to direct you to
the proper controller. However, you can easily mess up the route and it
could go to a completely different controller.
A simple example would be the http://localhost/Products/List. When this
URL is requested, the routing engine analyzes the Products/List. It
doesnt look at the domain (http://localhost/) at all.
It takes the first segment of the URL and says, Since Products is the first
parameter and my route url template says the controller is the first
segment, Products is the name of the controller. It processes the List
segment the same way and identifies that the List action is in the Products
controller.
The id is the last piece of the URL segment. As you can see by the
defaults parameter, the Id is UrlParameter.Optional. This means we
may not have an id to pass into the action method (which is standard for
most routes).

The defaults parameter is equally important. Any one of these segments


could be missing from the URL. If they are missing, then we define that
default controller and default action in the MapRoute.
To crystallize this point a little further, this table is based off of the
MapRoute defined above for various URLs coming into the application.
If the Url is:

Controller

Action

Id

http://localhost/Products/Edit/5

Products

Edit

http://localhost/Products/Edit

Products

Edit

(empty)

http://localhost/Products

Products

Index

(empty)

Home

Index

(empty)

http://localhost/

As you can see, routing can get very elaborate, but here are some tips
when defining your routes in your application.
Keep your routes as simple as possible
If you have a large number of routes in your RouteConfig.cs, you are
doing it wrong. Remember, its a template for how your site is
structured. It should be a simple scheme on how to get to each
controller and action effectively and quickly.
Keep a Route Table handy in your RouteConfig.cs
Its usually a good idea to have some sample URLs as comments in
your RouteConfig.cs to visually confirm that a specific URL is
pointing to the proper route.
Map out a sample URL
By all means, copy and paste the format from the table above into
your RouteConfig.cs as a comment, use a sample URL from your
application, and map out each component to confirm that they are
heading to the right controller through the right route.
Google-Friendly URLs

To take routing one step further, lets create user-friendly and Googlefriendly URLs.
How would you create a route for this URL?
http://www.danylkoweb.com/Blog/aspnet-mvc-routing-play-traffic-copwith-your-routes-90

We know that we are going to a BlogController (because of the Blog


directly after the domain). But I dont think we have an aspnet-mvcrouting-play-traffic-cop-with-your-routes-90 Action Method in our
controller.
So how do you map this route?
routes.MapRoute(
name: "BlogDetail",
url: "Blog/{title}-{id}",
defaults: new { controller = "Blog", action = "Detail", title =
String.Empty, id = UrlParameter.Optional }
);

Anytime we have a Blog name, it automatically goes to the BlogController


class.
Notice the {title}? Its a way to name the route parameter. You can
decide whether you want to use it or not. It doesnt even need to be a
parameter in the Action method.
We dont have an action in the URL template, but in the defaults, we tell
the routing engine to always use Detail action method in the Blog
controller.
The only wild card here is the id. Its the key to loading the proper blog
post. We tack the Id onto the end of the URL to know exactly which post to
load. Our Detail action method would look like this:
public ActionResult Detail(string id)
{
// Load the post based on the Id passed in (id=90)
var model = GetPostById(id);
return View(model);
}

I know routing was a large section, but as I mentioned before, it is


definitely an important part of ASP.NET MVC.
Essentially, the routing engine is sophisticated enough to take complex
URLs and break them down into simpler components for proper routing,
thus making your application easier to maintain.

Moving forward with enhancements

Now that we have the essentials under our belt, we can move towards
some additional features that not only assist you with your development,
but actually enhance your application and speed up your development
efforts.
Model Enhancements
Model Binders

In WebForms, when you submitted a form, a postback occurred and you


would assign all of the user control data from Form into your models
properties and fields.
In ASP.NET MVC, this process has been abstracted out to a separate
process called ModelBinding. Behind the scenes, when you submit a form
through a submit button, a default ModelBinder is available to map all of
the form data from the View into the current ViewModel and pass that
into your controller.
Most of the time, you dont need to worry about ModelBinders because
MVC out-of-the-box takes care of those pesky details for you.
Since we have a default modelbinder, we will create a new one just to show
you how easy it is to build a ModelBinder for your needs in case you need
one.
Lets say we wanted to create a Search capability in our web application.
Our search model would look like this:
public class SearchViewModel
{
public string SearchTerm { get; set; }
public IEnumerable Posts { get; set; }
}

To attach a ModelBinder to a ViewModel, we need a ModelBinder


attribute on the class.
[ModelBinder(typeof(SearchViewModelBinder))]
public class SearchViewModel
{
public string SearchTerm { get; set; }
public IEnumerable Posts { get; set; }
}

Our new SearchViewModelBinder would look like this:


public class SearchViewModelBinder: DefaultModelBinder
{
public override object BindModel(ControllerContext
controllerContext,
ModelBindingContext bindingContext)
{
var request = controllerContext.HttpContext.Request;
// Can use:
//
request.QueryString for binding Url Data
//
request.Cookies for browser cookie information
//
request.Form for form data posted back.
var searchTerm = request.Form["q"]; // Access the form
variable "q" (for query).
return new SearchViewModel
{
SearchTerm = searchTerm
};
}
}

When the postback occurs, it checks to see if there is a ModelBinder


available for this ViewModel in the current View. If not, it uses the
DefaultModelBinder. Since we have a custom ModelBinder and we have
the attribute attached to the top of our ViewModel, our custom
ViewModeBinder will get called.
After the model is created and returned, its passed on to the
corresponding controller as a parameter to the Action method and your
SearchController would look similar to this:
public class SearchController : Controller
{
public ActionResult Search(SearchViewModel model)
{
// model.SearchTerm would have the q value from the Form
(View).
// Make a database call to populate your post records.
// model.Posts = LoadPostsFromDatabase(model.SearchTerm);
return View(model);
}
}

ModelBinders are great for retrieving data from a postback, and you want
to populate a ViewModel so you dont have to muddy up your
controllers.

Validation Attributes

Your models are very secluded and keep to themselves.


We need a way to tell the View how to display them, tell the Controller
how to validate them, which fields are required, and what labels to show in
the form.
Validation Attributes are attributes meant for your Views to find out more
about your models. One specific Validation Attribute is the
RequiredAttribute.
The Required attribute tells the Controller (and View) that this field
cannot be blank and it needs to be filled in.
There are a number of additional validation attributes you can use on your
model properties. For example, there is a CreditCardAttribute you could
use for validating the entry of a credit card.
Each one is explained at the MSDN site under the DataAnnotations web
page.
In our previous example, we had our SearchViewModel. The SearchTerm
can have a Required attribute attached to it as shown below:
public class SearchViewModel
{
[Required]
[Display(Name = "Search Term:")]
public string SearchTerm { get; set; }
public IEnumerable Posts { get; set; }
}

We also included a DisplayAttribute for a label next to the search text box.
Now that the Required and Display attribute is on the SearchTerm
property, our View will know how to display a label for the SearchTerm
and Controller will know how to validate and show an error message if its
empty.
If we had an HtmlHelper wanting to display a label:
@Html.LabelFor(e=> e.SearchTerm)

the View would look at the SearchTerm property on the Model, examine it,
and notice that we have a Display attribute attached to the property. The
LabelFor would display Search Term: as the label in the View.
Controller Enhancements
Additional ActionResults

We mentioned earlier that we have a number of ActionResults we can use


in a controller, and we focused on one called the ViewResult to return our
HTML Views.
There are times when we dont want to return just a ViewResult. We would
rather return content, a file, or even JSON (JavaScript Object Notation).
You may have noticed in the Controller section above, in the
TryViewModel section, that we checked if the TryUpdateModel was
successful, and if it was, we called the Redirect(/Users/List)
ActionResult. The Redirect ActionResult does just that. It redirects the
user to a new page.
While you can create your own ActionResult, you already have a library of
them at your disposal.
ActionResult

Description

How Its Used

ViewResult

Returns an specific HTML View with


optional data.

public ActionResult List()


{
// processing done here.
return View "List");
}

RedirectResul
t

Performs a Redirect to send a user to


another View (or page).

public ActionResult List()


{
// processing done here.
return RedirectToAction("Create");
}
-ORpublic ActionResult List()
{
// processing done here.
return
Redirect "http://www.cnn.com/);
}

ActionResult

Description

How Its Used

JsonResult

Returns a JSON (JavaScript Object


Notation) formatted object. Best used in
JavaScript code to return data to the
browser.

public ActionResult GetJson()


{
var user = new User();
user.FirstName = "JD";
return Json(user,
JsonRequestBehavior.AllowGet);
}

FileResult

Returns a file to the browser.

public ActionResult GetFile()


{
var stream = new
StreamReader("yourFileToReturn.txt");
return File(stream.ReadToEnd(),
"text/plain");
}

ContentResult

Returns raw, unformatted data to the


browser.

public ActionResult SayHello()


{
var greeting = "Hello World.";
return Content(greeting);
}

There are other ActionResults out there to assist you in your development
efforts, and they are extremely easy to use. You make a call to an action
method in a controller, process the data, and return an ActionResult.
There are even ExcelActionResults some users have created to return
dynamic Excel spreadsheet data.
View Enhancements
UrlHelpers

Action UrlHelper
Links make the Internet world go around. And MVC applications are no
exception to the rule.
How can you properly manage an entire list of links for an application?
In your Views, you may have a link that points to a controller/action for
viewing products.
<a href="/Products/List" title="Go to a list of
products">Products</a>

While this is the hard way of writing your applications URLs, UrlHelpers
are a better way to build links. They help you with the rendering of your
links in your application. They are merely extension methods.
One specific UrlHelper is the Action UrlHelper. The Action UrlHelper can
replace the HTML link above with:
<a href="@Url.Action("List", "Products")" title="Go to a list of
products">Products</a>

While it looks like it may be a little bit bigger and include more code, it
definitely simplifies a link in your MVC application.
When you first start working with MVC, you can run into an ugly issue
with links in your Views relatively quickly. Let me set up a scenario for
you.
Lets say you have a 1,000-page website and your boss comes in and says
we are renaming the ProductsController to ClearanceController. What
happens to all of your links in your 1,000 pages on your site?
Its a 404 party and youre invited broken links all over the place.
You have 1,000 pages where you need to open each one, change the
Products controller name to Clearance, and save the changes 1,000
times (or do a major find/replace in your Views).
The best solution when you are using UrlHelpers is to centralize all of your
links.
For our solution, create a new folder off the root directory called Helpers
and a new folder under that called Url. Create a new extension method
class called ApplicationUrlHelpers.cs and it will look like this:
public static class ApplicationUrls
{
public static string ProductListUrl(this UrlHelper helper)
{
return helper.Action("List", "Products");
}
}

Note how we abstracted the Action method out of the View and into its
own link class. With this in place, your View will look a little different now.

<a href="@Url.ProductListUrl()" title="Go to a list of


products">Products</a>

What did we achieve by doing this? A lot:


1. We have centralized all of the links in our application to one unit
(ApplicationUrlHelpers.cs).
2. This allows IntelliSense to provide a pick list of which URL we want
to use in our Views.
3. You dont need to change all 1,000 pages of content. In your
ProductListUrl extension method, you change Products to
Clearance and youre done.
UrlHelpers make managing your links a little easier to work with in your
application.
RouteUrl UrlHelper
One last note about UrlHelper:. There is another method on the UrlHelper
class called RouteUrl.
The UrlHelper.RouteUrl is a faster option for building links. Why?
Remember the Routing section above and when I mentioned that too
many routes can slow down your application?
While the UrlHelper.Action method uses magic strings to create the
URL, the RouteUrl uses the RouteTable (created using the MapRoute
method) as a reference to create the URL.
If you have a smaller list of routes for your application, how fast is it to
look through a list of one? RouteUrl is definitely a faster approach to link
creation in your MVC applications.
If you decided to use RouteUrl, Your ApplicationUrlHelpers.cs would have
these changes.
public static string ProductListUrl(this UrlHelper helper)
{
return helper.RouteUrl("Default", new { @controller =
"Products", @action = "List" });
}

What were doing is forcing a link to be created based on an existing route


in the RouteTable.
The first parameter is the name of the route in the RouteTable (remember
BlueberryPie?).
The second parameter are the values we want for the controller, action,
and the optional id if we want to use it.
The best part about this is that you can choose which one is better for your
application and your links wont change in your Views. It still creates the
correct link and points to the proper controller and action.
HtmlHelpers

If youve used WebForms before and you are using ASP.NET MVC now,
you may be missing your server-side controls.
In MVC, the HtmlHelpers are what I would consider the equivalent of the
server-side controls.
HtmlHelpers are similar to UrlHelpers, but instead of returning a link,
HtmlHelpers return generated HTML or Views.
Of course, there are already a number of HtmlHelpers included with your
ASP.NET MVC framework.
You have already been exposed to the TextBox HtmlHelper and the
Html.BeginForm() in the TryUpdateModel section above.
@Html.TextBox("FirstName")

All this does is render out a simple textbox with an id and name of
FirstName.
<input type="text" id="FirstName" name="FirstName" />

These two lines are exactly the same.


Some of the other HtmlHelpers are listed below.
HtmlHelper

Description

@Html.TextBoxFor(e=> e.FirstName)

Renders a strongly-typed TextBox based on the Views

HtmlHelper

Description
Model.

@Html.ActionLink("Product Link", "List",


"Products")

Creates and renders an HTML link using a controller,


action, and link text to place inside the anchor.

@using (Html.BeginForm("Index", "Search",


FormMethod.Post, new {@id = "menuAdd",
@role = "form"}))

Renders the start of a form tag with settings on which


controller and action to send the data to and which method
to use when posting the data
(FormMethod.Post/FormMethod.Get).

@Html.CheckBox("MyCheckbox", true)

Create a Checkbox named MyCheckbox.

@Html.CheckBoxFor(e=> e.IsAvailable)

Renders a strongly-typed Checkbox based on a property in


the Views Model.

@Html.HiddenFor(e=> e.SearchTerm)

Renders a strongly-typed Hidden field based on a property


in the Views Model.

@Html.Hidden("SearchTerm",
Model.SearchTerm)

Creates a Hidden field named SearchTerm with a value


from the Model.

@Html.TextArea("BigTextArea")

Creates a TextArea called BigTextArea.

@Html.TextAreaFor(e=> e.BigText)

Renders a strongly-typed TextAreaFor field based on a


property in the Views Model.

If you wanted to create your own HtmlHelper, its just as easy as creating a
UrlHelper.
Create a directory under your Helpers directory called HTML, and create a
ApplicationHtmlHelpers.cs file.
As an example, lets create an HtmlHelper that displays a message if there
arent any posts.
public static class EmptyRecordsExtensions
{
public static MvcHtmlString NoPostsMsg(this HtmlHelper helper,
IEnumerable list)
{
if (list.Any()) return MvcHtmlString.Empty;
var container = new TagBuilder("div");
container.AddCssClass("well text-center");
container.InnerHtml = "No posts are available right now.";

return
MvcHtmlString.Create(container.ToString(TagRenderMode.Normal));
}
}

For an extension method to work properly, the class and the


method have to be static and you need the this statement in the signature
of the method.
We are receiving a list of posts and deciding whether or not any are in
there. If its a full list, we just return nothing.
If we dont have a list of posts, we create a DIV tag and add some CSS to
the div element. We then set out text inside the div to let the user know
that No posts are available right now.
Finally, we return the HTML back to the View.
To use our new HtmlHelper, we include the namespace at the top of the
View and add it to our View where we want the message to appear.
@Html.NoPostsMsg(Model.Posts)

If no posts are available, the message will appear. If we do have posts, it


wont display anything and we can display all of our posts.
PartialViews

As you continue to write more Views, youll start to notice patterns where
you are reusing your HTML over and over again.
PartialViews are shared, smaller pieces of HTML reused across your entire
site. It allows your site to remain consistent in its design.
For example, if you had a menu on every single page, you can take that
HTML and place it into the Views/Shared folder and call it
CommonMenu.cshtml.
To call that menu from your View, you would have the following:
@Html.Partial("CommonMenu")

ASP.NET MVC would search through the Views folder and finally find the
CommonMenu.cshtml in the Views/Shared folder and load the HTML to
display the menu.
If your ViewModel has a list of menus, you can pass the list of menus into
your partial view.
@Html.Partial("CommonMenu", Model.Menus)

Its a quick and easy way to make HTML modules out of your web
application.

Conclusion
With these firm fundamentals of ASP.NET MVC, you can now start
building your own basic websites using all of the best practices addressed
in this tutorial.
As with any subject, there is always more to learn.
Yet, with everything that ASP.NET MVC has to offer, it is constantly
evolving and is always exciting to see it turn into something better.
I hope you have enjoyed this tutorial and will post any feedback letting me
know if it was helpful.

Potrebbero piacerti anche