So the moment that I was waiting for has finally arrived. I picked up my courage and pressed the publish button of the .NET Core project. Then soon after, this happened:

The main message from this chart is that before publishing this version, the average response time of the site running on ASP.NET MVC 5 was between 90 and 100ms. With ASP.NET Core 2.2 we can see that the average is now around the 20ms mark.

That’s an improvement factor of 5X, and the best part of it is that there is minimal change in code. And this code is mainly updating references from System.Web to Microsoft.AspNetCore (see my previous blog entries to know more about these tweaks). And I also have to admit that this is running on the smallest paid tier.

So now the fun part starts, trying to improve the performance even further through my own changes.

As promised last time, I’m going to talk about HTML helpers on ASP.NET Core. If you’ve never heard about helpers before you may be thinking about another kind of helper perhaps:

However, I’m talking about the kind of helper which helps you write code once and re-use it in multiple places. The two biggest advantages with helpers are that they keep the page output consistent and when the time comes to change something you only need to change it in one place. Code repetition is not good and never fun.

Tag helpers seem to be the cool thing to do with .NET Core, but remember that I am looking to convert the site from MVC 5 with minimal changes and if possible do everything without touching the views.

The first required adjustment was to change the helper type to IHtmlHelper and the return type to IHtmlContent. That is good since in both cases the change is minimal, and because by using interfaces the helper is not directly dependant on any particular type as long as the type implements the interface.

The major problem that I faced was with the TagBuilder’s InnerHtml property: it’s readonly. No big worries though. Once I noticed that this was a dead end, changing the helper to build the content via a StringBuilder instead proved to be very easy. The last fix was to change the return object from MvcHtmlString to HtmlString.

Once that was done, it was just a case of running the solution and this permitted me to confirm that everything was actually working as wanted.

One final step that I want to sneak in is adding Application Insights to the solution. We are almost done with the changes, so adding monitoring to start gathering some data makes sense at this point. There is also tooling in Visual Studio to help with this. The easiest way is to right-click on the project and Add > Application Insights. Since I was already using Insights on the previous solution, this was also detected and the existing instrumentation key was easily picked up and configured by the editor. Next time, let’s get ready to publish the changes!

After migrating the layout page, it’s time to start working on the individual pages. I will start with the About page since it is the easiest and contains just a view with some static text. It’s all a matter of copying the controller and the view from the MVC5 solution to the new one.

Next I’ll work on the software details page. I did plan to start originally with the software overview page, but since that page makes use of a custom HTML helper I have decided to keep that for the next post.

So again, I copy over the controller code and this time the view model as well. Inside the view, we need to change the reference to Microsoft.AspNetCore.Mvc instead of System.Web.Mvc. Everything else stays the same, apart from the response caching part, which in my case I had to change this:

[OutputCache(Duration = 3600, VaryByParam = "softwareId")]

to this:

[ResponseCache(Duration = 3600, VaryByQueryKeys = new string[] { "softwareId" })]

As with mostly everything else, every feature that we want to use needs to be declared explicitly in .Net Core. For caching we need to reference Microsoft.AspNetCore.ResponseCaching and set it up in the Startup class. Add app.UseResponseCaching(); before app.UseMvc... and services.AddResponseCaching(); before services.AddMvc().

Since I am also reading data from the data provider class, I will also need to add the required dependency injection configuration.

services.AddTransient<ISoftwareProvider, EFSoftwareProvider>();

Finally, I copy the views and notice that everything looks ok. It has to work, but then I realise that I did not set up the route to handle this detail page. However, adding a route is just as simple as it ever was:

routes.MapRoute(
                    "Software details", 
                    "Software/{softwareId}/{title?}",
                    new { controller = "Software", action = "Details" });

And once again, I managed to migrate a page in a few easy steps.

In my last post, I left with the solution running an ASP.Net Core page which showed some information by calling the data libraries which were previously used by a MVC5 solution. That solution was using the standard out-of-the-box layout, so now the next step for me is to copy the layout from the old site and see what modifications need to be done in order to get it running.

The easiest part is copying the _Layout.cshtml file, since the path is the same (Views/Shared).

The first difference that we encounter is in the location of the CSS and Javascript files. The CSS files are moved from the Content folder into the wwwroot/css folder, whilst Javascript files must be copied from the Scripts folder into the wwwroot/js folder.

The next difference again is related to these files. The old default way of bundling assets was to configure everything in the App_Start/BundleConfig.cs file. The required files would then be bundled and minified at runtime. The .Net Core way of handling this is to pre-compile the bundling and minification process, and is configured inside the bundleconfig.json file. This is the file as I currently set it up:

[
  {
    "outputFileName": "wwwroot/css/site.min.css",
    "inputFiles": [
      "wwwroot/css/todc-bootstrap.css",
      "wwwroot/css/site.css"
    ]
  },
  {
    "outputFileName": "wwwroot/js/site.min.js",
    "inputFiles": [
      "wwwroot/js/site.js"
    ],
    "minify": {
      "enabled": true,
      "renameLocals": true
    },
    "sourceMap": false
  }
]

Since the process is different, the link to the file itself is modified as well. Note below the option to add the version number to the file is now part of the framework, whereas before I had to write this functionality myself. Adding the version number makes sure that the latest CSS is always read from the server when there is a change rather than from cache.

<link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />

There is also the possibility to link to the source paths directly when debugging during the development process. This can be done with the Environment tag helper. In the first case below, the unminified file is used during development and the minified version during testing/production:

<environment include="Development">
<link rel="stylesheet" href="~/css/site.css" />
</environment>
<environment exclude="Development">
<link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
</environment>

For Javascript files, there is also additional functionality out of the box in the shape of a CDN fallback mechanism. In this way, there is an attempt to fetch the file from CDN first, test by confirming that a particular object was correctly initialised (in this case window.jQuery), and in case of failure fall back to fetching the file locally.

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"
asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
asp-fallback-test="window.jQuery"
crossorigin="anonymous"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=">
</script>

Those were the most interesting parts, the remaining change was not strictly related to the migration. Since there are known security issues on using Bootstrap 3.3.7, I upgraded to the latest 3.4.0 package. That’s it, the site is now running with the migrated layout.

running the solution with the new layout

In the last post, I’ve shared with you my wish to start converting the website to run on ASP.Net Core. There are essential key differences in how some of the things work between MVC5 and MVC on .Net Core, but doing the changes in a certain way will make the transition easier.

My solution was to add a new web project side by side with the existing one, both targeting .Net framework 4.7.2. The data model (Entity Framework) and data provider (where data is fetched from the database) projects in the solution will be reused. The website is a relatively small project, and I can start moving stuff over to the .Net Core version whilst keeping the old project available in the same solution. In this way, any required changes to the live site during this period can still be done and pushed to production without problems.

the 3 projects inside the existing solution

If we need to install the latest version of the .Net Core SDK, here is the place to find it: https://dotnet.microsoft.com/download/dotnet-core/2.2

The next step was to right-click on the solution and Add a new project. In the first screen I chose ASP.Net Core Web Application, and in the next one made sure to select the .Net Framework option:

adding a new MVC project

Once the project was created, it’s time to do a quick test to see that it’s running. Whenever we need to choose which project to run, click on the new project inside the solution explorer and choose the ‘Set as StartUp Project’ option.

.Net Core Welcome page

That was easy enough! What I wanted to do next was to try to read and display some actual data through the other data projects. I just reused the Home controller for now.

Since I need to instantiate objects from the other projects, I had to reference them. And sure enough, I can right-click on the Dependencies folder and select ‘Add Reference…’.

adding a reference to the other projects inside the same solution

The next step was to add the database connection string to the app.config file. That also proved to be very easy as I just copied it from the other project. I also had to add a reference to System.Data.Entity. The final step to connect to the database was to set up dependency injection for the database context and the required provider. This is done with the following code:

services.AddScoped(provider =>
{
    var connectionString = Configuration.GetConnectionString("edvellaDb");
    return new edvellaEntities(connectionString);
});

services.AddTransient();

Inside the HomeController, I added a constructor so that the GameProvider is automatically injected when required. The GameProvider is the provider that takes care to connect to the games database for the game section of the site.

public HomeController(IGameProvider gameProvider)
{
    _gameProvider = gameProvider;
}

public IActionResult Index()
{
    var quotes = _gameProvider.GetAllQuotes();
    return View(quotes);
}

Then in the Index page, I added the call to get all the game quotes stored in the database. This data is then passed on to the view, resulting in the quotes being shown on the home page.

the 3 projects inside the existing solution