MS ASP.NET MVC Certification course vs ASP.NET core

29 Jul 2017 in web | programming | dotnet |

When you do the MS exams to become a certified developer you may come across the 486 course. The supporting video for that is 4 years old and is based on MVC 4. This post keep the notes I made while working through the video’s content based on ASP.NET Core’s capabilities, to see whether the info in there still holds up. The code is checked in, the commits more or less follow the progress made in the videos on channel 9.

Video 1 - Introduction

To get ourselves an mvc app we can do

dotnet new web
dotnet add package Microsoft.AspnetCore.Mvc
dotnet add package Microsoft.AspnetCore.StaticFiles

I deliberately started fom an empty web project, because I am a purist like that. You could also do dotnet new mvc and have a ton of stuff and an application that can run. To be honest, what put me off where the bower references. I am not going to start getting aquainted with bower at this point int time.

Anyhow, you can hook up the MVC functionality yourself into the basic web application startup:

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

Then we need

  • Controllers and Views folders
  • A HomeController, an Index view, maybe a Layout

and off you go. So far, pretty much the same stuff.

Video 2 - Models

Entity Framework, really now? Oh well, I said I would follow along, so here we go:

dotnet add package Microsoft.EntityFrameworkCore.SqlServer

Now we wire up tha database stuff on Startup…

// In "ConfigureServices"
services.AddDbContext<ConferenceContext>(opt => opt.UseSqlServer(
  "Server=(localdb)\\mssqllocaldb;Database=Conferences;" +
  "Trusted_Connection=True;MultipleActiveResultSets=true"));

That local db file will btw end up in your user directory (i.e. ~), I was too lazy to parse through changing the default, so I stuck with it (With frameworks & dependencies like that, just go with the flow, you will thank me when you have time to go have drinks on the weekend)

The ConferenceContext is over here, then there is some initialization code, hand-written. I didn’t find that fancy DropCreateAlwaysDatabaseInitializer thing mentioned in the video, but this is about MVC, not EF, so I didn’t search very hard. The initializer will do fine, I’m sure. We call it in Startup’s Configure:

public void Configure(..., ConferenceContext dbContext)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        DbInitializer.Initialize(dbContext);
    }
}

By configuring the EF stuff into the in-built DI Container, we could just let the framework provide us with a db Context. You will often see in the videos that the context gets instantiated in the controllers. You won’t need to do that anymore. If you want to know more about EF on .NET Core, this page is a good starting point.

The ComponentModel namespace is around, so you can attribute your models like crazy, just like in the video.

Video 3- Controllers

All still basically the same stuff. There is now also an IActionResult interface if you want to implemen the Action Result yourself completely. Also the action / controller filters have more options to choose from…

  • Auth filters - user authorisation
  • Resource filters - Caching etc.
  • Action filters - run code before / after
  • Exception filters - as the name implies
  • Result filters - only when the wrapped action runs successfully

There are also async variants, it is all nicely explained over here.

Global filters are added in the Startup through the options object that you get when calling the corresponding overload of services.AddMvc(); inside ConfigureServices.

Video 4- Views

Html Helpers are still around, but what you may probably do these days is using the new Tag Helpers to write out such a form. This is looking somewhat like that:

<form asp-action="Create">
    <div asp-validation-summary="All"></div>
    <div class="row">
        <div class="one column">
          <label asp-for="Name"></label>
          <span asp-validation-for="Name"></span>
        </div>
        <div class="eight columns">
          <input asp-for="Name" />
        </div>
    </div>
    ...

One of the last commits introduces some custom layout tag helpers to test out the feature.

Partial Views seem more or less unchanged.

Video 5 - Javascript, Page updates etc.

There has been so much change with regard to how to do & use javascript for UIs in general and ASP.NET MVC in particular lately that I am not going into much detail here. You can still return partial views, but e.g. the Ajax helper you see in the video does not seem to be around.

Bundling and minification also seems to be supported as a build step rather than a runtime step. These days you may do this with a tool like webpack anyway, so there isn’t much need to do this via Visual Studio. Also, all that nice javascript out there isn’t made available via Nuget anymore but you’ll rather have to go to npm. Additionally, jQuery just isn’t as important anymore.

Video 6 - Web APIs.

Quite a few changes over here as well. Web API got folded into the ASP.NET Core effort. Apparently there is a compatibility shim to smooth migration of an existing application. But, if you start from scratch, my repo contains a minimal example of getting all Sessions or Speakers, together with content negotation.

Out of the box you only get support for json. For xml e.g. you need to

dotnet add package Microsoft.AspNetCore.Mvc.Formatters.Xml

And then in Startup.ConfigureServices

services.AddMvc(opts =>
{
  opts.OutputFormatters.Add(new XmlSerializerOutputFormatter());
});

Then, a simple controller which inherits from Controller, just like the MVC ones:

[Route("api/[controller]/[action]")]
public class DataController : Controller
{
    private readonly ConferenceContext _ctx;
    public DataController(ConferenceContext ctx) => _ctx = ctx;

    [HttpGet]
    public IEnumerable<Session> Sessions() => _ctx.Sessions.ToList();

    [HttpGet]
    public IEnumerable<Speaker> Speakers() => _ctx.Speakers.ToList();
}

Now, e.g. with curl you can make calls with the proper accept header:

curl localhost:5000/api/data/speakers -v -H "Accept: application/xml"

And, presto, api with content negotiation.

Deploying to Azure

I won’t go into the actual azure deploying, as there are so many ways to do it (e.g. deploying a container these days, etc.), but in the light of doing standard MVC apps, one can have a look on how to do Entity Framewrk migrations these days. We will do this in the command line, and for this we extend the dotnet-CLI by adding a “plugin” in the csproj file (don’t forget to dotnet restore afterwards):

<ItemGroup>
  <!-- stuff -->
  <DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.1" />
</ItemGroup>

When all works you should be able to run migration commands via dotnet ef ...

And then, when you are working without Visual Studio you may be subject to a number of issues:

Missing Microsoft.EntityFrameworkCore.Design

add it e.g. via dotnet add package and restore.

Connection string from appsettings.json not found:

The migrations thing actually runs from bin/..., so you need to make sure that your settings file ends up there as well.

Doing this manually, you need to add the following to the csproj file:

<ItemGroup>
  <Content Update="appsettings.json">
    <CopyToOutputDirectory>Always</CopyToOutputDirectory>
  </Content>
</ItemGroup>

Config object null

the migrations tool will actually run your ConfigureServices code from your startup, but NOT your Program’s Main. It is there where I had the code to instantiate a config. To resolve, I check in ConfigureServices that the Config object exists:

//Startup.cs
public void ConfigureServices(IServiceCollection services)
{
    Program.EnsureConfig();
    // ...
    services.AddDbContext<ConferenceContext>(opt => 
        opt.UseSqlServer(
            Program.Configuration.GetConnectionString("DefaultConnection")));
}

// Program.cs
public static void EnsureConfig()
{
    if (Configuration != null)
        return;

    var builder = new ConfigurationBuilder()
        .SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.json");

    Configuration = builder.Build();
}

From there on I was able to use the tool. Then we can create the initial setup:

dotnet ef migrations add InitialCreate
dotnet ef database update

which sets up a migration file (as well as a .designer.cs file? No idea…). Btw, for a documentation of the tool’s capabilities go to the Migrations docs

Chronology

  |  
comments powered by Disqus