A Look at ASP.NET 5: Part 4 - MVC 6


Posted by Shawn Wildermuth on Sep 14, 2015 on 15:53PM

measurewoodI had planned on finishing these a long time ago, but working on my Pluralsight course about ASP.NET 5 distracted me. Sorry about that.

If you’ve been doing web development in .NET, you probably have at least a passing experience with ASP.NET’s MVC framework. At it’s core, it’s a common way to build and architect web applications. The new stack is built on the same metaphors from the older versions. If you’ve been using MVC before, you won’t be lost and some of the additions are welcome.

I’ll explain what I’m doing in a series of blog posts and link them all here as I write them. The plan is to write posts about:

Part 1: Getting Started

Part 2: Startup

Part 3: Using Entity Framework 7

Part 4: ASP.NET MVC 6 (This Post)

Part 5: The API

Part 6: Web Tooling with VS2015 (coming soon)

The project will continue to evolve and you can always get the latest version by visiting it on GitHub:

https://github.com/shawnwildermuth/MyCountries

In this post I’m going to make an assumption you’ve used MVC before. Because of that, I’m not going to cover the why as much as the how and compare it to the earlier versions.

Configuring MVC

Out of the box with a new project, MVC get’s configured for you but let’s look at what that means and why you’d care.

In the ConfigureServices method (inside startup.cs), MVC is added to the service container using the innocuous AddMvc method:

// Add MVC services to the services container.
services.AddMvc();

This call, like much of what is in the ConfigureServices method is just about registering the service classes. But to actually support MVC, we need to go to the Configure method of the startup.cs and use the MVC functionality:

// Add MVC to the request pipeline.
app.UseMvc(routes =>
{
  routes.MapRoute(
        name: "default",
        template: "{controller}/{action}/{id?}",
        defaults: new { controller = "Home", action = "Index" });
});

It may seem odd to have to add it in both places, but again remember that ConfigureServices is about configuring the services, and Configure is about adding middleware to the pipeline. In fact the order is important in Configure:

// Add static files to the request pipeline.
app.UseStaticFiles();

// Add cookie-based authentication to the request pipeline.
app.UseIdentity();

// Add MVC to the request pipeline.
app.UseMvc(routes =>
{
  routes.MapRoute(
        name: "default",
        template: "{controller}/{action}/{id?}",
        defaults: new { controller = "Home", action = "Index" });
});
The order of these middleware calls are important as if MVC is called before Identity is called, then authorization is never reached. Though not a hard and fast rule, the last piece of middleware I’m using is usually the UseMvc call. You can see that you can configure routes directly in the UseMvc method like you’ve probably done in previous versions of MVC.

Controllers

Also like previous versions of MVC, a typical controller simply derives from the Controller class:

public class HomeController : Controller

Depending on how you were using dependency injection in your older MVC projects, it’s built into the platform now so you can inject services you need directly into the controller constructors:

  public class HomeController : Controller
  {
    private IEmailer _emailer;
    public HomeController(IEmailer emailer)
    {
      _emailer = emailer;
    }

Action methods in controllers are largely unchanged except they should be returning a IActionResult instead of an ActionResult (though both are supported). The IActionResult is a little cleaner and will be performed asynchronously:

public IActionResult Index()
{
  ViewBag.Message = "An example of ASP.NET 5";

  return View();
}

In the current version of the tooling (at least on my machine), all the “Create View” and “Go to View” integration seems to be missing, but that was just a shortcut anyway.

Views and Razor

Views are also pretty similar to what you used in prior versions. Razor is still the language of choice and most of the typical Razor syntax is support with some nice additions. But for most cases, the Razor you’re used to will just work. For example:

@model MyCountries.Web.Models.ContactModel
@{
  ViewBag.Title = "Contact";
}

@section Scripts {
  <script src="~/lib/jquery-validation/jquery.validate.js"></script>
  <script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
}

<!-- Main-bar Column -->
<div class="col-md-8 col-lg-8 col-md-offset-2 col-lg-offset-2">

  <h2>@ViewBag.Title.</h2>
  <h3>@ViewBag.Message</h3>

One thing that you can see in the new MVC is that they want to encourage more HTML-like decorators instead of having to use @Foo style razor as much. This is most starkly obvious with tag helpers. But before I show you them, let’s talk about a new file that can exist in the view folder:

SNAGHTML12edaa5b

The _ViewImports.cshtml file is where you can import assemblies and namespaces into all your views. This is the alternative to the old web.config method. By default, this file is how they’re adding tag helpers into the views, but it’s good to see what it looks like:

@using MyCountries.Web
@using MyCountries.Web.Models
@using Microsoft.AspNet.Identity
@addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers"

This is bringing in the three namespaces to all the views as well as adding the default tag helpers to each view. But what are tag helpers?

Tag Helpers

Like I mentioned, the team is trying to move to allow more HTML-attribute-like syntax if you want to use it. Tag Helpers are a more readable version of functionality like form input control, action links and the like. But your old style syntax for ActionLinks and Forms still work:

@using (Html.BeginForm("Register", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
{
  @Html.AntiForgeryToken()
  <h4>Create a new account.</h4>
  <hr />
  @Html.ValidationSummary(true, "", new { @class = "text-danger" })
  <div class="form-group">
    @Html.LabelFor(m => m.UserName, new { @class = "col-md-2 control-label" })
    <div class="col-md-10">
      @Html.TextBoxFor(m => m.UserName, new { @class = "form-control" })
    </div>
  </div>

But as an alternative, Tag Helpers allow you to make this much more concise with a syntax that is based on attributes that start with asp-*.

Let’s see tag helpers for action links:

<a  asp-controller="Home" asp-action="Index" class="br-red">
  <!-- Link Icon -->
  <i class="fa fa-home link-icon"></i>
  <!-- Link Title -->
  <span class="link-title">Home</span>
</a>

While we could have crafted an ActionLink or used the Url class to handle this, the tag helpers (the (asp-controller and asp-action attributes) are transformed to a href for us when this is rendered.

More interestingly for me is how clean it makes forms. We can use HTML mostly and get rid of that nasty “new { @class = “ syntax that it used to require. Here is a similar form using tag helpers:

<div class="contact-form">
  <form role="form" method="post">
  
    <div asp-validation-summary="ValidationSummary.ModelOnly"></div>
  
    <div class="form-group">
      <input asp-for="Name" class="form-control" placeholder="Name" />
      <span asp-validation-for="Name" class="text-danger"></span>
    </div>

    <div class="form-group">
      <input asp-for="Email" class="form-control" placeholder="Email" />
      <span asp-validation-for="Email" class="text-danger"></span>
    </div>

    <div class="form-group">
      <textarea asp-for="Comments" class="form-control" cols="40" rows="4" placeholder="Comments"></textarea
      <span asp-validation-for="Comments" class="text-danger"></span>
    </div>

    <div class="form-group">
      <!-- form Submit and reset button -->
      <input type="submit" class="btn btn-info" value="Submit" />
    </div>

  </form>
</div>

Note the different tag helpers allow us to specify the generation of the different input types without having to hack together the other attributes we need (e.g. class and placeholder). In this example you can see a number of these that are important:

asp-for: Used to specify properties are mapped the @model of the page. Works for labels and form contols.

asp-validation-for: Used to specify the text of the validation for a property of the @model

asp-validation-summary: Used to show the validation summary (for the model or the model and properties).

There are a number of other tag helpers that you can use but hopefully you can see the power of them on display here. I like this new model as it allows most of the pure-HTML approach to be used and just decorate it with the MVC helpers as necessary. It’s not as jarring to read either, though some will argue that it’s hard to see what is client-vs-server code here.

Whether you use tag-helpers or not is more a matter of style than rule, but I, personally, like them.

Next time, I’ll use the MVC 6 framework to expose an API and say goodbye to Web API reluctantly.




Application Name WilderBlog Environment Name Production
Application Ver 1.1.0.0 Runtime Framework .NETCoreApp,Version=v1.1
App Path D:\home\site\wwwroot Runtime Version .NET Core 4.6.24628.01
Operating System Microsoft Windows 6.2.9200 Runtime Arch X86