Friday, 22 May 2020

4 - ASP.NET to ASP.NET Core

What I thought would be a fairly straightforward change was upgrading from ASP.NET (framework) to ASP.NET Core. It actually took me a month to migrate our application.

Identity to Identity Core

I knew there would be authentication issues since ASP.NET Identity (which we used on our ASP.NET app) is a .NET Framework-only library.

Microsoft rewrote the library for .NET Standard/Core as ASP.NET Core Identity - this version is also the version used by a Blazor server-side template if you opt for the built-in authentication. However the database schema used by the Core version is modified so you need to upgrade. However, I was able to create a version of the database that included the new fields required for Core, but would also work against the legacy version. This meant I could test new the app using a copy of the live security database to ensure that when we migrated the live application, I could update the database and go.

It also meant that if disaster struck we could revert the live app back to using the .NET Framework version without having to reverse the database changes. Might be worth a separate article on this alone.

DI Model

One of the key differences between ASP.NET Framework and Core is the use of the services model and Dependency Injection (DI). Our old app had a number of 'techniques' (polite word for hacks) for  getting services, so a chunk of time was required to restructure controllers, services and code to work with this new approach. It's worth it though as the DI model is much simpler and makes unit testing much easier too.

JSON Serialization

Something I hadn't anticipated was that System.Text.Json is used as the Web API serializer by default in ASP.NET Core, and this caused a lot of problems with API calls from the client JavaScript.

Initially it was behaviours like camel-casing serialized names by default, e.g. a C# property "TestThis" is serialized as "testThis". This caused a number of API calls to fail as the deserialization on the client wasn't mapping properties on the client, where we had used the C# naming style.

Then other differences with serialization and deserialization of JSON kept cropping up - so eventually I gave up trying to fix lots of code, and switched to using Newtonsoft JSON:
      }).AddNewtonsoftJson( =>
      {
          // revert to original naming when serializing
          o.SerializerSettings.ContractResolver = new DefaultContractResolver();
          //

Resources and Scripts

In the ASP.NET Core model the static resources such as CSS, JavaScript and Images are all located in a new folder wwwroot. I took the upgrade as an opportunity to move our client-side resources such as Bootstrap, JQuery etc. to be loaded using LibMan. I'm not a fan of NPM and using that would probably also mean we'd need to implement build scripts such as Gulp or WebPack to copy files around. LibMan avoids this by allowing you to specify the libraries and even specific files and their destination.
Our own Scripts were located in a /Scripts folder using TypeScript, so I had reconfigure this to output the resulting JavaScript into the wwwroot folder. However when debugging we had an issue: the source TypeScript is only present in the /Scripts folder, which isn't part of the published application. I fixed that by amending the .csproj file with this section:
  <!-- copies TS source to wwwroot (only for Debug build) -->
  <!-- source: https://github.com/NuGet/Home/issues/6743 -->
  <Target Name="CopyTsToWwwRoot" BeforeTargets="Build" Condition="'$(Configuration)'=='Debug'">
    <Message Text="Copying scripts/ts to wwwroot" />
    <ItemGroup>
      <SourceTs Include="$(MSBuildProjectDirectory)\Scripts\**\*.ts" />
    </ItemGroup>
    <Copy SourceFiles="@(SourceTs)" DestinationFiles="@(SourceTs -> '$(MSBuildProjectDirectory)\wwwroot\scripts\%(RecursiveDir)%(Filename)%(Extension)')" />
  </Target>

This section will copy the .ts files into the target wwwroot folders and retain the folder structure, so the files are present.

The only thing I wasn't able to fix with LibMan was the loading of TypeScript type definitions, so I had to use NPM to load these. However these are development-only to enable TypeScript compilation, so we didn't need to copy the files into wwwroot so I was able to avoid having to implement WebPack etc.

Web Jobs

Our app runs on Azure and we also hosted a number of background WebJobs to perform various long running tasks and process Azure queues. Although we could retain these I wanted to bring these into the web application using the IHostedService support in ASP.NET Core 3.1

This is actually much better than WebJobs since the code isn't located in the AppData folder and is easier to set up and control. It shares the same IConfiguration system as the main web application as well. It's not strictly required for upgrading to using Blazor but I'd strongly recommend using this service over WebJobs.

No comments:

Post a Comment

FAQ - Why isn't my component updating when I call <code>StateHasChanged</code> ?

Blazor updates the UI using a diffing algorith in the same way as other SPAs like Angular, React and Vue. This means that it does not re-r...