Migrating From ASP.NET MVC 5 to MVC 6

By Rick Anderson, Daniel Roth, Steve Smith, and Scott Addie

This article shows how to get started migrating an ASP.NET MVC 5 project to ASP.NET MVC 6. In the process, it highlights many of the things that have changed from MVC 5 to MVC 6. Migrating from MVC 5 to MVC 6 is a multiple step process and this article covers the initial setup, basic controllers and views, static content, and client-side dependencies. Additional articles cover migrating ASP.NET Identity models, and startup and configuration code found in many MVC 5 projects.

In this article:

Create the starter MVC 5 project

To demonstrate the upgrade, we’ll start by creating a new ASP.NET MVC 5 app. Create it with the name WebApp1 so the namespace will match the MVC 6 project we create in the next step.

../_images/new-project1.png ../_images/new-project-select-mvc-template.png

Optional: Change the name of the Solution from WebApp1 to Mvc5. Visual Studio will display the new solution name (Mvc5), which will make it easier to tell this project from the next project. You might need to exit Visual Studio and then reload the project to see the new name.

Create the MVC 6 project

Create a new empty MVC 6 web app with the same name as the previous project (WebApp1) so the namespaces in the two projects match. Having the same namespace makes it easier to copy code between the two projects. You’ll have to create this project in a different directory than the previous project to use the same name.

../_images/new-project-select-empty-aspnet5-template.png
  • Optional: Create a new MVC 6 app named WebApp1 with authentication set to Individual User Accounts. Rename this app FullMVC6. Creating this project will save you time in the conversion. You can look at the template generated code to see the end result or to copy code to the conversion project. It’s also helpful when you get stuck on a conversion step to compare with the template generated project.

Configure the site to use MVC

  • Open the project.json file and add Microsoft.AspNet.Mvc and Microsoft.AspNet.StaticFiles to the dependencies property and the scripts section as highlighted below:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
{
    "version": "1.0.0-*",
    "compilationOptions": {
        "emitEntryPoint": true
    },

    "dependencies": {
        "Microsoft.AspNet.StaticFiles": "1.0.0-rc1-final",
        "Microsoft.AspNet.Mvc": "6.0.0-rc1-final",
        "Microsoft.AspNet.IISPlatformHandler": "1.0.0-rc1-final",
        "Microsoft.AspNet.Server.Kestrel": "1.0.0-rc1-final"
    },

    "commands": {
        "web": "Microsoft.AspNet.Server.Kestrel"
    },

    "frameworks": {
        "dnx451": { },
        "dnxcore50": { }
    },

    "exclude": [
        "wwwroot",
        "node_modules"
    ],
    "publishExclude": [
        "**.user",
        "**.vspscc"
    ],
    "scripts": {
        "prepublish": [ "npm install", "bower install", "gulp clean", "gulp min" ]
    }
}

Microsoft.AspNet.StaticFiles is the static file handler. The ASP.NET runtime is modular, and you must explicitly opt in to serve static files (see Working with Static Files).

The scripts section is used to denote when specified build automation scripts should run. Visual Studio now has built-in support for running scripts before and after specific events. The scripts section above specifies NPM, Bower and Gulp scripts should run on the prepublish stage. We’ll talk about NPM, Bower, and Gulp later in the tutorial. Note the trailing ”,” added to the end of the publishExclude section.

For more information, see project.json and Introducing .NET Core.

  • Open the Startup.cs file and change the code to match the following:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Startup
{
    // This method gets called by the runtime. Use this method to add services to the container.
    // For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app)
    {
        app.UseIISPlatformHandler();

        app.UseStaticFiles();

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
    }

UseStaticFiles adds the static file handler. As mentioned previously, the ASP.NET runtime is modular, and you must explicitly opt in to serve static files. For more information, see Application Startup and Routing.

Add a controller and view

In this section, you’ll add a minimal controller and view to serve as placeholders for the MVC 5 controller and views you’ll migrate in the next section.

  • Add a Controllers folder.
  • Add an MVC controller class with the name HomeController.cs to the Controllers folder.
../_images/add_mvc_ctl.png
  • Add a Views folder.
  • Add a Views/Home folder.
  • Add an Index.cshtml MVC view page to the Views/Home folder.
../_images/view.png

The project structure is shown below:

../_images/project-structure-controller-view.png

Replace the contents of the Views/Home/Index.cshtml file with the following:

<h1>Hello world!</h1>

Run the app.

../_images/hello-world.png

See Controllers and Views for more information.

Now that we have a minimal working MVC 6 project, we can start migrating functionality from the MVC 5 project. We will need to move the following:

  • client-side content (CSS, fonts, and scripts)
  • controllers
  • views
  • models
  • bundling
  • filters
  • Log in/out, identity (This will be done in the next tutorial.)

Controllers and views

  • Copy each of the methods from the MVC 5 HomeController to the MVC 6 HomeController. Note that in MVC 5, the built-in template’s controller action method return type is ActionResult; in MVC 6, the templates generate IActionResult methods. ActionResult is the only implementation of IActionResult, so there is no need to change the return type of your action methods.
  • Delete the Views/Home/Index.cshtml view in the MVC 6 project.
  • Copy the About.cshtml, Contact.cshtml, and Index.cshtml Razor view files from the MVC 5 project to the MVC 6 project.
  • Run the MVC 6 app and test each method. We haven’t migrated the layout file or styles yet, so the rendered views will only contain the content in the view files. You won’t have the layout file generated links for the About and Contact views, so you’ll have to invoke them from the browser (replace 2468 with the port number used in your project).
../_images/contact-page.png

Note the lack of styling and menu items. We’ll fix that in the next section.

Static content

In previous versions of MVC (including MVC 5), static content was hosted from the root of the web project and was intermixed with server-side files. In MVC 6, static content is hosted in the wwwroot folder. You’ll want to copy the static content from your MVC 5 app to the wwwroot folder in your MVC 6 project. In this sample conversion:

  • Copy the favicon.ico file from the MVC 5 project to the wwwroot folder in the MVC 6 project.

The MVC 5 project uses Bootstrap for its styling and stores the Bootstrap files in the Content and Scripts folders. The template-generated MVC 5 project references Bootstrap in the layout file (Views/Shared/_Layout.cshtml). You could copy the bootstrap.js and bootstrap.css files from the MVC 5 project to the wwwroot folder in the new project, but that approach doesn’t use the improved mechanism for managing client-side dependencies in ASP.NET 5.

In the new project, we’ll add support for Bootstrap (and other client-side libraries) using Bower:

  • Add a Bower configuration file named bower.json to the project root (Right-click on the project, and then Add > New Item > Bower Configuration File). Add Bootstrap and jquery to the file (see the highlighted lines below).
{
    "name": "ASP.NET",
    "private": true,
    "dependencies": {
        "bootstrap": "3.3.5",
        "jquery": "2.1.4"
    }
}

Upon saving the file, Bower will automatically download the dependencies to the wwwroot/lib folder. You can use the Search Solution Explorer box to find the path of the assets.

../_images/search.png

See Manage Client-Side Packages with Bower for more information.

Gulp

When you create a new web app using the ASP.NET 5 Web Application template, the project is setup to use Gulp. Gulp is a streaming build system for client-side code (HTML, LESS, SASS, etc.). The included gulpfile.js in the project contains JavaScript that defines a set of gulp tasks that you can set to run automatically on build events or you can run manually using the Task Runner Explorer in Visual Studio. In this section, we’ll show how to use the MVC 6 template’s generated gulpfile.js file to bundle and minify the JavaScript and CSS files in the project.

If you created the optional FullMVC6 project (a new ASP.NET MVC 6 web app with Individual User Accounts), add gulpfile.js from that project to the project we are updating. In Solution Explorer, right-click the web app project and choose Add > Existing Item.

../_images/addExisting.png

Navigate to gulpfile.js from the new ASP.NET MVC 6 web app with Individual User Accounts and add the add gulpfile.js file. Alternatively, right-click the web app project and choose Add > New Item. Select Gulp Configuration File, and name the file gulpfile.js. Replace the contents of the gulp file with the following:

/// <binding Clean='clean' />
"use strict";

var gulp = require("gulp"),
    rimraf = require("rimraf"),
    concat = require("gulp-concat"),
    cssmin = require("gulp-cssmin"),
    uglify = require("gulp-uglify");

var paths = {
    webroot: "./wwwroot/"
};

paths.js = paths.webroot + "js/**/*.js";
paths.minJs = paths.webroot + "js/**/*.min.js";
paths.css = paths.webroot + "css/**/*.css";
paths.minCss = paths.webroot + "css/**/*.min.css";
paths.concatJsDest = paths.webroot + "js/site.min.js";
paths.concatCssDest = paths.webroot + "css/site.min.css";

gulp.task("clean:js", function (cb) {
    rimraf(paths.concatJsDest, cb);
});

gulp.task("clean:css", function (cb) {
    rimraf(paths.concatCssDest, cb);
});

gulp.task("clean", ["clean:js", "clean:css"]);

gulp.task("min:js", function () {
    return gulp.src([paths.js, "!" + paths.minJs], { base: "." })
        .pipe(concat(paths.concatJsDest))
        .pipe(uglify())
        .pipe(gulp.dest("."));
});

gulp.task("min:css", function () {
    return gulp.src([paths.css, "!" + paths.minCss])
        .pipe(concat(paths.concatCssDest))
        .pipe(cssmin())
        .pipe(gulp.dest("."));
});

gulp.task("min", ["min:js", "min:css"]);

The code above performs these functions:

  • Cleans (deletes) the target files.
  • Minifies the JavaScript and CSS files.
  • Bundles (concatenates) the JavaScript and CSS files.

See Using Gulp with ASP.NET 5 and Visual Studio.

NPM

NPM (Node Package Manager) is a package manager which is used to acquire tooling such as Bower and Gulp; and, it is fully supported in Visual Studio 2015. We’ll use NPM to manage Gulp dependencies.

If you created the optional FullMVC6 project, add the package.json NPM file from that project to the project we are updating. The package.json NPM file lists the dependencies for the client-side build processes defined in gulpfile.js. Right-click the web app project, choose Add > Existing Item, and add the package.json NPM file. Alternatively, you can add a new NPM configuration file as follows:

  1. In Solution Explorer, right-click the project.
  2. Select Add > New Item.
  3. Select NPM Configuration File.
  4. Leave the default name: package.json.
  5. Click Add.

Open the package.json file, and replace the contents with the following:

{
  "name": "ASP.NET",
  "version": "0.0.0",
  "devDependencies": {
    "gulp": "3.8.11",
    "gulp-concat": "2.5.2",
    "gulp-cssmin": "0.1.7",
    "gulp-uglify": "1.2.0",
    "rimraf": "2.2.8"
  }
}

Right-click on gulpfile.js and select Task Runner Explorer. Double-click on a task to run it.

For more information, see Client-Side Development in ASP.NET 5.

Migrate the layout file

  • Copy the _ViewStart.cshtml file from the MVC 5 project’s Views folder into the MVC 6 project’s Views folder. The _ViewStart.cshtml file has not changed in MVC 6.
  • Create a Views/Shared folder.
  • Copy the _Layout.cshtml file from the MVC 5 project’s Views/Shared folder into the MVC 6 project’s Views/Shared folder.

Open _Layout.cshtml file and make the following changes (the completed code is shown below):

  • Replace @Styles.Render(“~/Content/css”) with a <link> element to load bootstrap.css (see below)
  • Remove @Scripts.Render(“~/bundles/modernizr”)
  • Comment out the @Html.Partial(“_LoginPartial”) line (surround the line with @*...*@) - we’ll return to it in a future tutorial
  • Replace @Scripts.Render(“~/bundles/jquery”) with a <script> element (see below)
  • Replace @Scripts.Render(“~/bundles/bootstrap”) with a <script> element (see below)

The replacement CSS link:

<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />

The replacement script tags:

<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>

The updated _Layout.cshtml file is shown below:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@ViewBag.Title - My ASP.NET Application</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
</head>
<body>
    <div class="navbar navbar-inverse navbar-fixed-top">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                @Html.ActionLink("Application name", "Index", "Home", new { area = "" }, new { @class = "navbar-brand" })
            </div>
            <div class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li>@Html.ActionLink("Home", "Index", "Home")</li>
                    <li>@Html.ActionLink("About", "About", "Home")</li>
                    <li>@Html.ActionLink("Contact", "Contact", "Home")</li>
                </ul>
                @*@Html.Partial("_LoginPartial")*@
            </div>
        </div>
    </div>
    <div class="container body-content">
        @RenderBody()
        <hr />
        <footer>
            <p>&copy; @DateTime.Now.Year - My ASP.NET Application</p>
        </footer>
    </div>

    <script src="~/lib/jquery/dist/jquery.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
    @RenderSection("scripts", required: false)
</body>
</html>

View the site in the browser. It should now load correctly, with the expected styles in place.

Configure Bundling

The MVC 5 starter web template utilized the MVC runtime support for bundling. In ASP.NET MVC 6, this functionality is performed as part of the build process using Gulp. We’ve previously configured bundling and minification; all that’s left is to change the references to Bootstrap, jQuery and other assets to use the bundled and minified versions. You can see how this is done in the layout file (Views/Shared/_Layout.cshtml) of the full template project. See Bundling and Minification for more information.