facebook

Blog

Stay updated

Some tricks to not go crazy while migrating
Migrating a Blazor Wasm Hosted project from .NET Core 3.1 to .NET 5
Tuesday, April 13, 2021
Blazor su .NET5

One of the goals of the milestone we are managing for one of our customers is the migration from .NET Core 3.1 to .NET 5. Let’s see together the steps we have taken and analyze the corrective measures adopted to mitigate some breaking changes. 

The project in question is a Blazor Wasm application with a .NET designed backend. The Data Access Layer is realized with Entity Framework Core.

1. Let’s’ start from dependencies

Nowadays there are no projects that do not have some dependence on one or more external libraries. We ensure that all the dependencies of our project are compatible with the new version of the framework. In this regard, remember that . NET 5 supports all libraries that support . NET Standard up to version 2.1. In our case, we had a few but fundamental dependencies all developed with . NET Standard 2.0 and then we started advantaged. 

The first piece of advice I share is to prefer, among the external libraries, those that implement .NET Standard with a good reputation and support. 

2. Update Blazor

Once verified that all our dependencies would be usable even after the framework’s upgrade, we can start the update of the various projects. Let’s start with the backend project and make the following changes:

  • Update the global.json file with the new SDK version
{ 
  "sdk": { 
	"version": "5.0.102" 
   } 
} 
  • Update the TargetFrameowrkof the project’s file 
<PropertyGroup> 
<TargetFramework>net5.0</TargetFramework> 
</PropertyGroup> 
  • Update Blazor Server packages
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="5.0.2" /> 
  • Update all other dependencies to the version that supports the Framework . NET 5 excluding EF Core. 

The procedure is not particularly complex and does not present any criticisms.  

 Let’s now move on to the frontend project (Blazor Web Assembly), where some small change is necessary but not complicated.  

The necessary steps to upgrade the Blazor front end are: 

  • Update the TargetFrameowrkof the project’s file
<PropertyGroup> 
        <TargetFramework>net5.0</TargetFramework> 
</PropertyGroup>
  • Update Blazor Client packages
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="5.0.2" /> 
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="5.0.2" PrivateAssets="all" /> 
 <PackageReference Include="Microsoft.Extensions.Http" Version="5.0.0" /><PackageReference Include="System.Net.Http.Json" Version="5.0.0" /> 
  • Modify index.html file

Remove

<app>Loading...</app> 

Add

<div id="app">Loading...</div> 
  • Modify App.razor file
<Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true"> 
  • Modify Program.cs file
var builder = WebAssemblyHostBuilder.CreateDefault(args); 
builder.RootComponents.Add<App>("#app"); 
  • Update all other dependencies to the version that supports the Framework . NET 5

Once the procedure is completed, our application is updated to the new version of the framework, and we can start using all the new features that the framework makes available to us. For more information on exceptional cases, you can take a look at the official guide.

3. Update Entity Framework

To complete our project’s migration, we must now face a choice: continue to use Entity Framework in version 3.1 or complete the migration path with the upgrade to version 5.0 of EF Core? 

 It might seem a trivial choice: since we are updating the latest version of the framework, why not update the “Dependency” to the newest version? 

Given that Entity Framework Core is a . NET Standard 2.0 library since version 3.1, both versions are therefore supported in . NET 5. Unfortunately, however, Entity Framework Core 5.0 presents a series of changes to the LINQ provider for SQL Server (that we use in our project), which may impact our application both in terms of functionality and performance. 

Fortunately, our project is equipped with a good suite of tests and load tests, so we decide to upgrade Entity Framework Core to version 5.0.  

Technically being an external library, what we did was update the package version in the project file.

<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.2" /> 
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.2" /> 
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.2"> 

We launched the tests, and as expected, many of them did not pass. We didn’t lose heart and went to analyze the various failed tests. We identified some critical issues due to the new version’s breaking changes to which we were prepared. Some undocumented ones that we discovered and faced along the way by doing good developers. One of the innovations introduced in Entity Framework Core 5 is the Split Queries.

The LINQ to SQL provider converts LINQ queries into T-SQL code to run on the DBMS and maps the result of these queries to the entities defined in the context. 

Using the Split Queries option, the LINQ provider will generate small N queries for each Table involved using the Join as a filter on the linked tables. This way, both the data recovery phase from the DBMS and the subsequent mapping phase of the domain entities can be paralleled and optimized. A small example is taken from the documentation to understand the mechanism better. 

var artists = context.Artists         
     .Include(e => e.Albums)  
    .ToList(); 

will generate the code T-SQL

SELECT a."Id", a."Name", a0."Id", a0."ArtistId", a0."Title" 
FROM "Artists" AS a 
LEFT JOIN "Album" AS a0 ON a."Id" = a0."ArtistId" 
ORDER BY a."Id", a0."Id" 

Instead

var artists = context.Artists 
        .Include(e => e.Albums) 
        .AsSplitQuery() 
        .ToList(); 

will generate the T-SQL code

SELECT a."Id", a."Name" 
FROM "Artists" AS a 
ORDER BY a."Id" 
  
SELECT a0."Id", a0."ArtistId", a0."Title", a."Id" 
FROM "Artists" AS a 
INNER JOIN "Album" AS a0 ON a."Id" = a0."ArtistId" 
ORDER BY a."Id" 

By default, the split queries is disabled, and you can use it individually on each query with the Assplitquery() method or globally in the ‘Onconfiguring (documentation). 

It is also possible to enable a warning that at runtime signals queries that potentially could benefit from an increase in performance by enabling the Split Queries. 

services.AddDbContext<ItinerisDbContext>(opt => 
        	{ 
                opt.UseSqlServer(Configuration.GetConnectionString("SqlServerConnectionString")); 
                opt.ConfigureWarnings(s => s.Log(RelationalEventId.MultipleCollectionIncludeWarning)); 
            }); 

Another novelty introduced in EF Core is the ability to filter the Include es:

var blogs = context.Blogs 
    .Include(e => e.Posts.Where(p => p.Title.Contains("Cheese"))) 
	.ToList(); 

The two novelties we have just seen introduce an undocumented breaking change, which our tests have brought to light.  Queries that produce projections on different collections with the Select instruction must also contain all the Include needed for the projection, otherwise, the properties of the linked collections will always be “null.”

No problem, as we introduced the necessary Include the failed tests have finally passed. 

Once the problems highlighted by the unit tests have been resolved, we launch the load tests, and the results were not exactly up to expectations. In some cases, we found a performance degradation compared to version 3.1, even 50%

To make the tuning of queries, we use another novelty of Entity Framework Core 5: the log in the queries executed. Through this mechanism, we can analyze and optimize them.  

The log is enabled in the following way:

services.AddDbContext<ItinerisDbContext>(opt => 
{ 
… 
opt.LogTo(l => Console.WriteLine(l)); 
}); 

Analyzing the warnings and the logs provided, we launched a tuning session of the code. Careful use of the split query and the use of specific methods that the SQL Server provider makes available (for example, Like() ) allow us to mitigate the decline in performance without reaching the previous version’s levels.

Conclusions

In conclusion, while the upgrade to the new version in the framework has generated tangible benefits both in terms of performance and fresh features, EF Core’s upgrade was neither painless and fast nor free of imprecations. 

I recommend the upgrade of EF Core in those projects where you have good coverage of unit tests to identify all the points to change automatically. If the project is not equipped with tests, arm yourself with such patience and pay particular attention to the methods you recover from the DB.

See you in the next article

Written by

Antonio Venditti