Loading...

Upgrading My ASP.NET Core Projects to 3.1

Software
Mar 16, 2020
#csharp
#asp.net
#.net core
Avatar
Author
Matt Kruskamp

.NET Core 3.1 is LTS meaning it’s a great time to upgrade projects to the newest version. This article goes through the process of converting one of my existing .NET Core 1.1 projects to 3.1. I chose this project because updating from .NET Core 1.1 is a bit more complicated than 2.0. Also, the source code for this project is available, so anyone interested can view the commit log.

The first step is installing the .NET Core 3.1 SDK. If you’re using Visual Studio, it’s as simple as updating to the latest version. If you aren’t using Visual Studio, go download the SDK.

Project files

Once the dependancies are in order, edit the .csproj for the application.

<PropertyGroup>
  <TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>

Under the TargetFramework section netcoreapp1.1 turns to netcoreapp3.1. This step is the same as all the .NET Core versions.

<PropertyGroup>
  <TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>

Next up is the project references. NET Core consolidated several packages, so a few PackageReference elements get removed. The full details of consolidated packages are available in the upgrade documentation.

<ItemGroup>
  <PackageReference Include="BuildBundlerMinifier" Version="2.4.337" />
  <PackageReference Include="Microsoft.AspNetCore" Version="1.1.1" />
  <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.2" />
  <PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="1.1.1" />
  <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.1" />
  <PackageReference Include="Microsoft.VisualStudio.Web.BrowserLink" Version="1.1.0" />
</ItemGroup>

In my case, I was able to remove all but one of the packages.

<ItemGroup>
  <PackageReference Include="BuildBundlerMinifier" Version="2.4.337" />
</ItemGroup>

Next, I upgraded the remaining NuGet packages to their latest versions. The old versions are below.

<ItemGroup>
  <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
  <PackageReference Include="xunit" Version="2.2.0" />
  <PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
</ItemGroup>

I did this part with the JetBrains Rider Nuget Package Manager. You can use Visual Studio, or do it manually. The project results are below.

<ItemGroup>
  <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
  <PackageReference Include="xunit" Version="2.4.1" />
  <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
</ItemGroup>

Code updates

At this point, nothing’s going to work. There are a few differences between .NET Core 1.1 and 3.1, so I started at the top. Open the Program.cs.

public static void Main(string[] args)
{
    var host = new WebHostBuilder()
        .UseKestrel()
        .UseContentRoot(Directory.GetCurrentDirectory())
        .UseIISIntegration()
        .UseStartup<Startup>()
        .Build();

    host.Run();
}

.NET Core 1.1 explicitly calls UseKestrel, and .Net 2.2 uses the WebHostBuilder. .NET Core 3.0 uses IHostBuilder. This is a quick fix.

public static void Main(string[] args)
{
    CreateHostBuilder(args).Build().Run();
}

private static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

The next update is in Startup.cs. The first update is in the Startup method.

public Startup(IHostingEnvironment env)
{
    var builder = new ConfigurationBuilder()
        .SetBasePath(env.ContentRootPath)
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
        .AddEnvironmentVariables();
    Configuration = builder.Build();
}

.NET Core 1.1 requires the JSON files to be added with code. Settings get inferred when injecting IConfiguration.

public Startup(IConfiguration configuration)
{
    Configuration = configuration;
}

Now the ConfigureService method.

public void ConfigureService(IServiceCollection services)
{
    services.AddMvc();
}

AddControllersWithViews replaces AddMvc.

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();
}

The Configure method’s next. Here’s the original.

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseBrowserLink();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }

    app.UseStaticFiles();

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

Logging’s configured via appSettings.json, so adding a logger factory isn’t necessary. Also, endpoint management isn’t done with UseMvc anymore.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        //app.UseBrowserLink();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }

    app.UseStaticFiles();
    app.UseRouting();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");
    });
}

Configuration

Now the project builds, but it isn’t going to run. The appsettings.json file must be updated.

{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Warning"
    }
  }
}

The logging section has a couple of changes for hosting lifetime.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

These changes must be propogated to the appsettings.Development.json file as well.

{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information"
    }
  }
}

Update to the same configuration specified previously.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  }
}

Finally, my .NET Core 1.0 project didn’t have a launchSettings.json file in it.

{
  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:43023"
    }
  },
  "profiles": {
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "Cyberkruz.PublishProfile.Web": {
      "commandName": "Project",
      "launchBrowser": true,
      "applicationUrl": "http://localhost:5000",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}

Conclusion

At this point the project ran like a champ. I’ll be upgrading all my projects, and I’ll post any gotchas I happen to find. Upgrading these projects isn’t terribly difficult, but it does take a bit of precision work. Unit tests help. Hopefully, this can help someone out.

4 min read
Share this post:

Comments

comments powered by Disqus

Related Articles

All articles
Software Mar 25, 2019

Fun Build - Hide Your Slack Link

Winter made me a little stir-crazy. In order to deflect the brain spirals, I wanted to make a little side project.

Jan 1, 2019

Rentler

Rentler’s next-generation online property management tools are designed by landlords, for landlords. We’ve taken everything excellent about renting properties and made it better, and we’ve taken everything difficult about being a landlord making it more accessible.

Software Jun 19, 2018

Using Serilog with Concurrency Keys

In a previous article I wrote about using middleware to add concurrency keys into an Asp.Net Core project. I’d like to extend that and show how to integrate a logging platform with concurrency keys.

The pixel mine

I'm currently the Co-Founder / CTO with an amazing team at Rentler. We build property management software for independant landlords and their renters using a plethora of technologies.

View work
Illustration Illustration
Top