ASP.Net 5 is a major change to the Microsoft web development ecosystem. As I write this, Release Candidate 1 is the current version available. You can expect some minor changes to things, but it’s quite baked at this point. Much of what you know about ASP.Net will need to be relearned. The changes bring ASP.Net more on par with the rest of the web development world.

There are several changes you need to know about. First, WebForms are gone. Everything is now MVC-based. Also, web controllers and Web API are now combined. VB is not supported. Another big change is that you can develop ASP.NET web applications on Windows via Visual Studio or on Windows, Mac, or Linux using Visual Studio Code. You can also run your site on Windows, Mac, or Linux thanks to the .NET Execution Environment (DNX).NET Core, and the new Kestrel server. You can still host on Windows Server in IIS or your Windows developer machine with IIS Express.

One big change is the removal of System.Web. Over the years, as new ASP.NET features have been added, they have been put in this assembly. It had become bloated. Many features were no longer used in modern web apps. And because of the bloat, it used a lot of memory. Everything is now modular and excluded by default.

Creating the project

But, let’s get on with what this post is about…creating your first web application with the new tooling.

  1. Install ASP.NET 5. Instructions for Windows, Mac, and Linux are here. Even if you are running Visual Studio 2015, you will need to get the latest bits. This walk through uses VS 2015 on Windows.
  2. From the VS menu, select File > New > Project.
  3. In the New Project Dialog, select Visual C# > Web. Make sure .NET Framework 4.6 is selected then choose ASP.Net Web Application. image
  4. Select OK to save your selected options and display the New ASP.Net Project dialog.
    image
  5. Under ASP.NET 5 Templates, select Web Application. Click OK to create the project. Visual Studio will work for a bit, then you’ll see the new project. But it looks different than you’re used to.
    image

You should now have a new ASP.NET 5 project, ready to use.

Understanding the project changes

Let’s pause for a moment to talk about these changes.

  • The src folder – This is the source folder. All the files needed to compile and build your web application.
  • What happened to .csproj? The project file is a Visual Studio file. Because Visual Studio doesn’t run on Mac or Linux, it has been removed and replaced with project.json. If you open this file you’ll see a list of dependencies, runtimes, scripts, and other files and settings.
  • Frameworks listed in project.json – Support of non-Windows platforms requires changes to the .NET runtime (CLR). The dnx451 is the full “Dot NET eXecution Environment”. Think of this as the full Windows runtime. To run on Mac, Linux, or even a reduced instruction set on Windows, use dnxcore50.
  • project.lock.json – This file contains a complete list of all the NuGet packages used in the project. In ASP.Net 5, only .Net assemblies are installed via NuGet. Javascript, css, html, and other types of files are installed and managed through a different mechanism explained in a moment.
  • wwwroot folder – Static files such as javascript, css, html, images, etc go here.
  • Dependencies folder – Non .NET dependencies are stored here and installed and managed via Bower, an open source dependency management tool that is used across platforms and web tooling. In other words, it’s NuGet for web front end packages.

    There is also an npm folder. This is the Node Package Manager. It’s the package manager for web backend packages.

  • global.json – Used by Visual Studio to store project information. Notice it sits outside the src folder.
  • appsettings.json – Application settings such as connection strings are put here.
  • gulp.js – Gulp is a widely used, open source build system. Think of this as being kind of like the old MSBuild file.
  • startup.cs – The entry point for your application.

Now that you know what the new files and folders are, it’s time to setup the database.

Creating the database

ASP.NET 5 uses Entity Framework 7 by default. The big change here is that the designer has been deprecated and is no longer available. It’s all EF Code First now.

You probably also noticed there is now a Models and View Models folder. For production systems, I move all the models to a Data project, but to make this introduction easier, I’ll leave the models in their current folder.

Open the appsettings.json file and you’ll see the connection string. As in earlier versions of ASP.NET MVC, localdb is used by default. I will leave these settings. I will also keep this introduction very simple by using a single table.

  1. Right-click on the Models folder and create a new class named Customer.
  2. Create the Customer class
       1: public class Customer

       2: {

       3:     public int Id { get; set; }

       4:     public string FirstName { get; set; }

       5:     public string LastName { get; set; }

       6:     public string Street1 { get; set; }

       7:     public string Street2 { get; set; }

       8:     public string City { get; set; }

       9:     public string State { get; set; }

      10:     public string ZipCode { get; set; }

      11: }

  3. Save the file
  4. Open the ApplicationDbContext.cs file and add a DbSet line for the Customer class.
       1: public class ApplicationDbContext : IdentityDbContext<ApplicationUser>

       2: {

       3:     protected override void OnModelCreating(ModelBuilder builder)

       4:     {

       5:         base.OnModelCreating(builder);

       6:         // Customize the ASP.NET Identity model and override the defaults if needed.

       7:         // For example, you can rename the ASP.NET Identity table names and more.

       8:         // Add your customizations after calling base.OnModelCreating(builder);

       9:     }

      10:  

      11:     public DbSet<Customer> Customers { get; set; }

      12: }

  5. Save the updated file.

The database will be created on first use. For now, we’ll move on to creating the forms.

The Customer Controller

Creating the Customer Controller is quite easy. We’ll use MVC scaffolding to do it.

  1. Right-click on the Controllers folder and select Add –> Controller. The Add Scaffold dialog is displayed.
    image
  2. Select MVC 6 Controller with views, using Entity Framework. Then click Add. The Add Controller dialog is displayed.
    image
  3. For Model class, select the Customer model.
  4. Next select the Data context class, ApplicationDbContext.
  5. Click Use async controller actions. Using async controllers is a good idea as it makes your application perform better.
  6. Click Add to create the CustomersController and views.

Open CustomersController.cs and you will see something new.

   1: public class CustomersController : Controller

   2: {

   3:     private ApplicationDbContext _context;

   4:  

   5:     public CustomersController(ApplicationDbContext context)

   6:     {

   7:         _context = context;    

   8:     }

   9:  

  10:     // GET: Customers

  11:     public async Task<IActionResult> Index()

  12:     {

  13:         return View(await _context.Customers.ToListAsync());

  14:     }

  15:  

  16:     // GET: Customers/Details/5

  17:     public async Task<IActionResult> Details(int? id)

  18:     {

  19:         if (id == null)

  20:         {

  21:             return HttpNotFound();

  22:         }

  23:  

  24:         Customer customer = await _context.Customers.SingleAsync(m => m.Id == id);

  25:         if (customer == null)

  26:         {

  27:             return HttpNotFound();

  28:         }

  29:  

  30:         return View(customer);

  31:     }

  32:  

  33:     // GET: Customers/Create

  34:     public IActionResult Create()

  35:     {

  36:         return View();

  37:     }

  38:  

  39:     // POST: Customers/Create

  40:     [HttpPost]

  41:     [ValidateAntiForgeryToken]

  42:     public async Task<IActionResult> Create(Customer customer)

  43:     {

  44:         if (ModelState.IsValid)

  45:         {

  46:             _context.Customers.Add(customer);

  47:             await _context.SaveChangesAsync();

  48:             return RedirectToAction("Index");

  49:         }

  50:         return View(customer);

  51:     }

  52:  

  53:     // GET: Customers/Edit/5

  54:     public async Task<IActionResult> Edit(int? id)

  55:     {

  56:         if (id == null)

  57:         {

  58:             return HttpNotFound();

  59:         }

  60:  

  61:         Customer customer = await _context.Customers.SingleAsync(m => m.Id == id);

  62:         if (customer == null)

  63:         {

  64:             return HttpNotFound();

  65:         }

  66:         return View(customer);

  67:     }

  68:  

  69:     // POST: Customers/Edit/5

  70:     [HttpPost]

  71:     [ValidateAntiForgeryToken]

  72:     public async Task<IActionResult> Edit(Customer customer)

  73:     {

  74:         if (ModelState.IsValid)

  75:         {

  76:             _context.Update(customer);

  77:             await _context.SaveChangesAsync();

  78:             return RedirectToAction("Index");

  79:         }

  80:         return View(customer);

  81:     }

  82:  

  83:     // GET: Customers/Delete/5

  84:     [ActionName("Delete")]

  85:     public async Task<IActionResult> Delete(int? id)

  86:     {

  87:         if (id == null)

  88:         {

  89:             return HttpNotFound();

  90:         }

  91:  

  92:         Customer customer = await _context.Customers.SingleAsync(m => m.Id == id);

  93:         if (customer == null)

  94:         {

  95:             return HttpNotFound();

  96:         }

  97:  

  98:         return View(customer);

  99:     }

 100:  

 101:     // POST: Customers/Delete/5

 102:     [HttpPost, ActionName("Delete")]

 103:     [ValidateAntiForgeryToken]

 104:     public async Task<IActionResult> DeleteConfirmed(int id)

 105:     {

 106:         Customer customer = await _context.Customers.SingleAsync(m => m.Id == id);

 107:         _context.Customers.Remove(customer);

 108:         await _context.SaveChangesAsync();

 109:         return RedirectToAction("Index");

 110:     }

 111: }

Notice the constructor beginning at line 5. The context is passed in rather than being created in the controller as it was in MVC 5. The big change MVC 6 is that Dependency Injection (DI) is used throughout. By default a simple DI system is used. But you can replace it with your favorite DI tool.

What happened to Razor?

If you created ASP.NET 5 forms, you were used to working with Razor syntax that looked something like this:

   1: @model Dashboard.Models.RegisterViewModel

   2: @{

   3:     ViewBag.Title = "Register";

   4: }

   5:  

   6: <h2>@ViewBag.Title.</h2>

   7:  

   8: @using (Html.BeginForm("Register", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))

   9: {

  10:     @Html.AntiForgeryToken()

  11:     <h4>Create a new account.</h4>

  12:     <hr />

  13:     @Html.ValidationSummary("", new { @class = "text-danger" })

  14:     <div class="form-group">

  15:         @Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" })

  16:         <div class="col-md-10">

  17:             @Html.TextBoxFor(m => m.Email, new { @class = "form-control" })

  18:         </div>

  19:     </div>

  20:     <div class="form-group">

  21:         @Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" })

  22:         <div class="col-md-10">

  23:             @Html.PasswordFor(m => m.Password, new { @class = "form-control" })

  24:         </div>

  25:     </div>

  26:     <div class="form-group">

  27:         @Html.LabelFor(m => m.ConfirmPassword, new { @class = "col-md-2 control-label" })

  28:         <div class="col-md-10">

  29:             @Html.PasswordFor(m => m.ConfirmPassword, new { @class = "form-control" })

  30:         </div>

  31:     </div>

  32:     <div class="form-group">

  33:         <div class="col-md-offset-2 col-md-10">

  34:             <input type="submit" class="btn btn-default" value="Register" />

  35:         </div>

  36:     </div>

  37: }

This code is used to create the Register form. Razor syntax is the @Html code. In ASP.NET 5 and MVC 6, Razor syntax has been replaced with “Tag Helpers”. Here’s the ASP.NET 5 version of Register.cshtml.

   1: @model RegisterViewModel

   2: @{

   3:     ViewData["Title"] = "Register";

   4: }

   5:  

   6: <h2>@ViewData["Title"].</h2>

   7:  

   8: <form asp-controller="Account" asp-action="Register" method="post" class="form-horizontal" role="form">

   9:     <h4>Create a new account.</h4>

  10:     <hr />

  11:     <div asp-validation-summary="ValidationSummary.All" class="text-danger"></div>

  12:     <div class="form-group">

  13:         <label asp-for="Email" class="col-md-2 control-label"></label>

  14:         <div class="col-md-10">

  15:             <input asp-for="Email" class="form-control" />

  16:             <span asp-validation-for="Email" class="text-danger"></span>

  17:         </div>

  18:     </div>

  19:     <div class="form-group">

  20:         <label asp-for="Password" class="col-md-2 control-label"></label>

  21:         <div class="col-md-10">

  22:             <input asp-for="Password" class="form-control" />

  23:             <span asp-validation-for="Password" class="text-danger"></span>

  24:         </div>

  25:     </div>

  26:     <div class="form-group">

  27:         <label asp-for="ConfirmPassword" class="col-md-2 control-label"></label>

  28:         <div class="col-md-10">

  29:             <input asp-for="ConfirmPassword" class="form-control" />

  30:             <span asp-validation-for="ConfirmPassword" class="text-danger"></span>

  31:         </div>

  32:     </div>

  33:     <div class="form-group">

  34:         <div class="col-md-offset-2 col-md-10">

  35:             <button type="submit" class="btn btn-default">Register</button>

  36:         </div>

  37:     </div>

  38: </form>

  39:  

  40: @section Scripts {

  41:     @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }

  42: }

Tag Helpers are the asp- construct inside the HTML tags. This makes ASP.NET 5 look more like other HTML and Javascript libraries. It makes the code look more like HTML. Open the Customer Create.cshtml to see with it looks like in this new version.

   1: @model WebApplication2.Models.Customer

   2:  

   3: @{

   4:     ViewData["Title"] = "Create";

   5: }

   6:  

   7: <h2>Create</h2>

   8:  

   9: <form asp-action="Create">

  10:     <div class="form-horizontal">

  11:         <h4>Customer</h4>

  12:         <hr />

  13:         <div asp-validation-summary="ValidationSummary.ModelOnly" class="text-danger"></div>

  14:         <div class="form-group">

  15:             <label asp-for="City" class="col-md-2 control-label"></label>

  16:             <div class="col-md-10">

  17:                 <input asp-for="City" class="form-control" />

  18:                 <span asp-validation-for="City" class="text-danger" />

  19:             </div>

  20:         </div>

  21:         <div class="form-group">

  22:             <label asp-for="FirstName" class="col-md-2 control-label"></label>

  23:             <div class="col-md-10">

  24:                 <input asp-for="FirstName" class="form-control" />

  25:                 <span asp-validation-for="FirstName" class="text-danger" />

  26:             </div>

  27:         </div>

  28:         <div class="form-group">

  29:             <label asp-for="LastName" class="col-md-2 control-label"></label>

  30:             <div class="col-md-10">

  31:                 <input asp-for="LastName" class="form-control" />

  32:                 <span asp-validation-for="LastName" class="text-danger" />

  33:             </div>

  34:         </div>

  35:         <div class="form-group">

  36:             <label asp-for="State" class="col-md-2 control-label"></label>

  37:             <div class="col-md-10">

  38:                 <input asp-for="State" class="form-control" />

  39:                 <span asp-validation-for="State" class="text-danger" />

  40:             </div>

  41:         </div>

  42:         <div class="form-group">

  43:             <label asp-for="Street1" class="col-md-2 control-label"></label>

  44:             <div class="col-md-10">

  45:                 <input asp-for="Street1" class="form-control" />

  46:                 <span asp-validation-for="Street1" class="text-danger" />

  47:             </div>

  48:         </div>

  49:         <div class="form-group">

  50:             <label asp-for="Street2" class="col-md-2 control-label"></label>

  51:             <div class="col-md-10">

  52:                 <input asp-for="Street2" class="form-control" />

  53:                 <span asp-validation-for="Street2" class="text-danger" />

  54:             </div>

  55:         </div>

  56:         <div class="form-group">

  57:             <label asp-for="ZipCode" class="col-md-2 control-label"></label>

  58:             <div class="col-md-10">

  59:                 <input asp-for="ZipCode" class="form-control" />

  60:                 <span asp-validation-for="ZipCode" class="text-danger" />

  61:             </div>

  62:         </div>

  63:         <div class="form-group">

  64:             <div class="col-md-offset-2 col-md-10">

  65:                 <input type="submit" value="Create" class="btn btn-default" />

  66:             </div>

  67:         </div>

  68:     </div>

  69: </form>

  70:  

  71: <div>

  72:     <a asp-action="Index">Back to List</a>

  73: </div>

  74:  

  75: @section Scripts {

  76:     <script src="~/lib/jquery/dist/jquery.min.js"></script>

  77:     <script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>

  78:     <script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>

  79: }

As you probably guessed, it is cleaner and easier to read than MVC 5.

Update the menu

Before we run the application, let’s add a menu item for Customers.

  1. Open _Layout.cshtml in the Views\Shared folder.
  2. Add a Customers menu item. Make sure you use a new Tag Helper. See line 5 below.
       1: <ul class="nav navbar-nav">

       2:     <li><a asp-controller="Home" asp-action="Index">Home</a></li>

       3:     <li><a asp-controller="Home" asp-action="About">About</a></li>

       4:     <li><a asp-controller="Home" asp-action="Contact">Contact</a></li>

       5:     <li><a asp-controller="Customers" asp-action="Index">Customers</a></li>

       6: </ul>

Now you’re ready to run the application. Press F5 to compile and run. Then navigate to the Customers Index page. If you get an error screen about Database errors, follow the instructions on the error page. Once things are up and running, try adding a few customer records.

Startup.cs

Before wrapping up, I need to tell you about startup.cs. This is the entry point to the application.  It’s important you know about this file because in ASP.NET 5, everything is turned off by default, even static pages (see line 73 below where static pages are enabled).

   1: public class Startup

   2: {

   3:     public Startup(IHostingEnvironment env)

   4:     {

   5:         // Set up configuration sources.

   6:         var builder = new ConfigurationBuilder()

   7:             .AddJsonFile("appsettings.json")

   8:             .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);

   9:  

  10:         if (env.IsDevelopment())

  11:         {

  12:             // For more details on using the user secret store see http://go.microsoft.com/fwlink/?LinkID=532709

  13:             builder.AddUserSecrets();

  14:         }

  15:  

  16:         builder.AddEnvironmentVariables();

  17:         Configuration = builder.Build();

  18:     }

  19:  

  20:     public IConfigurationRoot Configuration { get; set; }

  21:  

  22:     // This method gets called by the runtime. Use this method to add services to the container.

  23:     public void ConfigureServices(IServiceCollection services)

  24:     {

  25:         // Add framework services.

  26:         services.AddEntityFramework()

  27:             .AddSqlServer()

  28:             .AddDbContext<ApplicationDbContext>(options =>

  29:                 options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));

  30:  

  31:         services.AddIdentity<ApplicationUser, IdentityRole>()

  32:             .AddEntityFrameworkStores<ApplicationDbContext>()

  33:             .AddDefaultTokenProviders();

  34:  

  35:         services.AddMvc();

  36:  

  37:         // Add application services.

  38:         services.AddTransient<IEmailSender, AuthMessageSender>();

  39:         services.AddTransient<ISmsSender, AuthMessageSender>();

  40:     }

  41:  

  42:     // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.

  43:     public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)

  44:     {

  45:         loggerFactory.AddConsole(Configuration.GetSection("Logging"));

  46:         loggerFactory.AddDebug();

  47:  

  48:         if (env.IsDevelopment())

  49:         {

  50:             app.UseBrowserLink();

  51:             app.UseDeveloperExceptionPage();

  52:             app.UseDatabaseErrorPage();

  53:         }

  54:         else

  55:         {

  56:             app.UseExceptionHandler("/Home/Error");

  57:  

  58:             // For more details on creating database during deployment see http://go.microsoft.com/fwlink/?LinkID=615859

  59:             try

  60:             {

  61:                 using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>()

  62:                     .CreateScope())

  63:                 {

  64:                     serviceScope.ServiceProvider.GetService<ApplicationDbContext>()

  65:                             .Database.Migrate();

  66:                 }

  67:             }

  68:             catch { }

  69:         }

  70:  

  71:         app.UseIISPlatformHandler(options => options.AuthenticationDescriptions.Clear());

  72:  

  73:         app.UseStaticFiles();

  74:  

  75:         app.UseIdentity();

  76:  

  77:         // To configure external authentication please see http://go.microsoft.com/fwlink/?LinkID=532715

  78:  

  79:         app.UseMvc(routes =>

  80:         {

  81:             routes.MapRoute(

  82:                 name: "default",

  83:                 template: "{controller=Home}/{action=Index}/{id?}");

  84:         });

  85:     }

  86:  

  87:     // Entry point for the application.

  88:     public static void Main(string[] args) => WebApplication.Run<Startup>(args);

  89: }

The ASP.NET has done a good job of explaining this file here.

Wrap up

There is A LOT of new to learn about ASP.NET 5 and MVC 6. As I’m writing this, almost everything is documented at http://docs.asp.net. If you see a topic with a wrench icon next to it, there is still work to be done on that section.

If you’re thinking of migrating and MVC 5 application, make sure you read this post by my friend Paul Litwin where he goes through a migration step by step.