To illustrate how to write Razor views in an ASP.NET MVC application, let’s revisit the WebMatrix blog website and rewrite it “the MVC way.” To start out, open up Visual Studio and choose File → New Project..., and then select the ASP.NET MVC 3 Web Application option. Name the new project MvcRazorBlog, as shown in Figure 4-1.
Then, from the New ASP.NET MVC 3 Project Wizard, select the Empty site template and the Razor View Engine (these should be the default settings); Figure 4-2 provides an example.
Once completed, your project should resemble the directory structure shown in Figure 4-3.
The blog site example from the WebMatrix chapter didn’t involve an official “model.” That is, you never created any classes to hold and manage data; all of the data interaction used method calls directly to the database and saved the results in local dynamic variables. WebMatrix can get away with accessing and using the data directly because of the page-based architecture it is built on; every page is responsible for its own data access (with assistance from helper objects) and for manipulating that data.
ASP.NET MVC’s architecture dictates that the Model and the View
are two separate entities, so in order to demonstrate the Razor syntax
within ASP.NET MVC’s Razor View Engine, you should create a model that
can hold and manage the site’s data. Since you’ve already implemented
the blog site once, you already know what data the site uses. To create
the Model for the ASP.NET MVC blog site, add a new class named Post
to the website’s Models folder, with the
following code:
namespace MvcRazorBlog.Models { public class Post { public long ID { get; set; } public string Title { get; set; } public string Body { get; set; } } }
Since the blog site doesn’t require very complex data, the
Post
class is all that’s needed at
this point. Once it’s in place, you can create the Controller that will
populate the Post
class with data
from the database and pass it to the View.
The default Empty site template chosen earlier expects a
controller named HomeController
,
which ASP.NET MVC adds to the Controllers folder by convention. To
create an empty controller, right-click on the Controllers folder and
select Add → Controller…, entering the name HomeController
.
The wizard will ask you if you’d like it to generate Create,
Update, Delete, and Details actions for you. We won’t use those actions
in this book, but feel free to let the wizard generate them for you if
you’re interested to see what they look like. The wizard will create a
new class file with one action named Index
:
using System.Web.Mvc; namespace MvcRazorBlog.Controllers { public class HomeController : Controller { public ActionResult Index() { return View(); } } }
According to ASP.NET MVC’s default routes, the HomeController
’s Index
action handles requests for the site’s home page (the root of the site
without specifying any file or folder names). The behavior we want to
implement in the Index
action directly correlates to
logic used in the sample WebMatrix site’s
Default.cshtml page in Chapter 2.
So, the next step is to reproduce the same data access logic that the
Default.cshtml page uses to
retrieve Post
data.
For better or worse, ASP.NET MVC does not offer the same
Database
object included in the
WebMatrix platform. Instead, Microsoft’s Entity Framework 4.1 (and up)
and its Code First functionality make data access just as easy as it
is in WebMatrix, or perhaps more so.
To get started with Entity Framework Code First, you’ll need to install it using the NuGet Package Manager, a Visual Studio extension installed as part of the ASP.NET MVC 3 install process.
After it’s installed, the only other step you need to take in
order to use the Entity Framework Code First framework is to create a
custom class that derives from the System.Data.Entity.DbContext
class, which
tells Entity Framework which objects you’d like to persist and
retrieve from the database. A working DbContext
implementation can be as simple as
the following:
using System.Data.Entity; using MvcRazorBlog.Models; public class BlogContext : DbContext { public DbSet<Post> Posts; }
This code extends the DbContext
class, exposing a single property
(Posts
) of type DBSet<Post>
. It is the DbSet<Post>
portion of this property
that indicates to Entity Framework that you’d like to use the Post
class as your model for data access and
that these objects can be found in the Posts table (corresponding to
the name of the property) in the database.
That’s all that’s needed in regards to setup and configuration; Entity Framework relies on “convention over configuration” to determine the rest. Entity Framework will even create the database for you if it does not exist when you attempt to access it!
Querying an Entity Framework Code First data context is even
easier than writing one. All you need to do is create an instance of a
DbContext
and start making standard
LINQ calls against the DbSet<TModel>
properties defined on
the context.
Thus, retrieving all of the Posts from the Blog database from
the HomeController
’s Index
action is trivial:
public class HomeController { public ActionResult Index() { var posts = new BlogContext().Posts; return View("Index"); } }
Once the Index
action retrieves the blog post
data from the database, it needs to select the appropriate View and
pass the post data to that View. Since the MVC pattern deliberately
decouples views and controllers, the controller cannot directly
communicate with the selected view to provide the view with the data
it needs. In order to get around this limitation, ASP.NET MVC relies
on an intermediary dictionary via a property named
ViewData
. Controllers and views both contain this
property, so it is easily accessible in both layers. Here is the
updated Index
action, adding a line of code to
assign the blog post data to the ViewData
dictionary for use in the views that you will create in the coming
sections:
public class HomeController { public ActionResult Index() { var posts = new BlogContext().Posts; ViewData["Posts"] = posts; return View("Index"); } }
Though passing values to views through the ViewData
dictionary works just fine, “magic
values” (string constants referenced in multiple places) like "Posts"
can become difficult to manage and
maintain. Additionally, the ViewData
dictionary is not strongly-typed,
so Visual Studio cannot provide you with IntelliSense on any values
passed into it. Instead of the ViewData
dictionary, the preferred approach
for passing data from controllers to views is through the model
parameter of the View()
helper
method.
Below you can see the Index
action updated to pass the Posts
data via the View()
helper
method:
- Index action retrieving blog posts and passing them to the view
public ActionResult Index() { var posts = new BlogContext().Posts; return View("Index", posts); }
At this point—even though you have not yet created the View
required to render HTML to the user—you should be able to verify that
this code works by placing a debug breakpoint on the final return View("Index");
line and hitting F5 to
run the website. If everything works, execution should stop at the
breakpoint and the posts
variable should contain a
list of Post
objects with the data
from your database!
To make website development easier and help developers be more productive, ASP.NET MVC relies on the concept of “convention over configuration” whenever possible. What this means is that, instead of relying on explicit configuration settings, ASP.NET MVC simply assumes that developers will follow certain conventions as they create website components.
The previous section applied several conventions when creating the
HomeController and its Index
action. The first
convention was placing the controller in the project’s Controllers
folder. Though not required for the application to compile and function,
keeping your controllers in the Controllers folder is a standard
practice. The second convention was the name of the controller, HomeController
. Since it is a very good
practice to give classes descriptive names, standard conventions
recommend applying the “Controller” suffix to controller class names.
When referring to the controller throughout the application, however, it
is much more straightforward to simply refer to “Home” instead of the
full name, “HomeController.”
The ASP.NET MVC framework addresses this conflict during the
application’s startup phase, when it searches through all of the
website’s assemblies, registering classes that extend ASP.NET MVC’s
ControllerBase
base class (using the
base class as yet another convention). As the framework locates classes
that derive from ControllerBase
, it
adds them to its internal dictionary using the class’s name—after
removing the “Controller” suffix—as the dictionary key. Thus, even
though the controller is named “HomeController,” the rest of the
application can simply refer to it as “Home.”
At first glance, the concept of convention over configuration may seem trivial. However, when taken altogether over the course of a large site, many of these seemingly small or meaningless optimizations can really add up to significant time savings, improved code readability, and increased developer productivity.
Get Programming Razor now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.