Why Migrate ASP.NET Legacy Apps to .NET Core?

Before we start discussing legacy .NET Framework to .NET Core migration, let us discuss what these frameworks are and their brief history.

.NET Framework is a software programming framework supporting languages like C#, F#, VB.NET, etc. The first version of .NET Framework (version 1.0) was released on February 13, 2002. Since then there have been multiple releases and many success stories around it. The last version 4.8 was released on April 18, 2019, and this is the last feature version. Going forward there will not be any feature upgrades to .NET Framework.

The reason for discontinuing any further development in the .NET Framework is the birth and tremendous success of .NET Core. These factors are causing many organizations to migrate to .NET core.

A Brief History of .NET Core

.NET Core is the open-source and cross-platform successor of .NET Framework. .NET Framework can run only in Windows, whereas .NET Core is built to run on every possible platform, including but not limited to Linux, Windows, macOS, and Raspberry Pi, to name a few.

.NET Core is licensed under the open-source MIT License. The first version of .NET Core was released on June 27, 2016. Since then six versions of .NET Core were released, with version 3.1 being the latest version. Due to being an open-source framework, it provides a faster and better opportunity for innovation.

With .NET Core 3.0, Windows Forms and WPF (Windows Presentation Framework) are now also available in .NET Core. This has allowed desktop applications to migrate to .NET Core, which they could not do earlier. Before .NET Core 3.0, we could only build server-side applications in .NET Core. Post .NET Core 3.0, from server-side to desktop, we can build any type of application in .NET Core.

The .NET Framework to .NET Core migration happened very fast, and for reasons we will discuss in detail later in this article. In the industry, organizations are converting their .NET Framework applications to .NET Core to take advantage of all the features it supports and improvements it promises.

But before going into the advantages of the .NET Core, let us discuss what are the major drawbacks of the legacy .NET Framework.

Drawbacks of .NET Framework

Limited Innovation

.NET Framework 4.8 is the last major release version of .NET Framework. Going forward there will not be any new feature implemented in .NET Framework. Only bug fixes and security patches will be provided. Meaning there will be no further innovation in the .NET Framework. Although Microsoft mentions that they will continue to support the .NET Framework, that does not mean making it compatible with the new and emerging technologies.

For example, if there is a new managed distributed database announced by AWS, it will not be guaranteed to have a NuGet package supporting the .NET Framework. Whereas it will definitely have support for .NET Core. The open-source community is very fast in adapting to new and emerging technologies, hence the possibility of a Nuget for .NET Core is higher compared to legacy .NET Framework.

Also, the new language changes in C# will only be available in .NET Core. Similarly, other innovations in Web (ASP.NET Core), as well as desktop frameworks (WPF), will happen only in .NET Core.

The Talent Pool

At present, the talent pool for .NET Framework developers is probably more compared to the .NET Core developers. However, developers are also joining the .NET Framework to .NET Core migration, and newer developers will not be interested in working with .NET Framework.

We did a little comparison of jobs in dice.com for .NET Core and .NET Framework. And as of writing this blog, for .NET Core, there are 3,467 jobs, whereas for .NET Framework there are 2,862 jobs. While this shows that companies are still looking for .NET Framework developers, more are seeking .NET Core skills.

Today most of the blog content and YouTube channel focus on .NET Core, making it the framework of choice for new developments.

Cost of Legacy Systems

The only option for running a Legacy ASP.NET Web application is in an IIS Server running on a Windows machine. Even if we do self-hosting of the ASP.NET Web application, we will still have to have it running inside a Windows Server. The cost of a Windows Server license is very high compared to a Linux Server.

For example, in AWS if we take a T3.2XLarge server, one running Windows and the other running Linux, the difference in monthly cost is approximately $107.75 (lower cost for Linux). Which gives us a cost-saving of $1,293 per year if we use Linux.

If we run the equivalent price comparison in Azure, then an A2 Windows Server monthly cost is $131.45. Whereas for an A2 Linux Server the monthly cost is $87.65. That is a $43.8 monthly savings. Annually a saving of $525.6.

Now, we can run an ASP.NET Core Web application on the Linux server and realize the savings.

Another important but not obvious saving for ASP.NET Core applications comes from using Docker containers. A legacy ASP.NET application can have only one instance running in a Windows Server, in which case it might not use the resources optimally. On the other hand, .NET Core applications can run in Docker containers, and we can have multiple containers running the same application or different applications inside a single Linux server, in turn utilizing the system resources more optimally. Hence optimizing cost.

The other cost is associated with not having enough innovation in legacy .NET Framework. Using the latest tools and frameworks becomes harder due to a lack of support. Hence, a lot of custom development and cost.

Security Risks

Given the fact that Microsoft has a complete focus on .NET Core and does not intend to make any feature changes to .NET Framework 4.8, there is always a risk of security running in .NET Framework versions. Though Microsoft clearly mentions that they will continue to provide security patches to .NET Framework 4.8, .NET Core always has the advantage given its open-source nature and being actively developed.

Advantages of .NET Core

Performance

ASP.NET Core Web Application is much faster than legacy ASP.NET Web Application. As of the day of writing this blog, based on tech empower performance tests, ASP.NET Core running on Kestrel Web Server on Linux, is 7th (Cloud test) and 8th (Physical box test) in the list of fastest Web applications. These tests are running on a Linux box. They also have legacy ASP.NET running in Mono in Linux (Nginx Web Server) as part of the tests. And legacy ASP.NET is currently number 351 on the list.

In Database connectivity, ASP.NET Core Web Application running on Kestrel Web Server on Linux and making a single SQL query in a PostgreSQL database is on number 28th of the list. For the same test, legacy ASP.NET running in Mono in Linux (Nginx Web Server), connecting to a MySql database and making a single SQL query comes in number 399 on the list.

For JSON serialization, the ASP.NET Core Web Application running on a Kestrel Web Server on Linux is number 11 in the list. Similarly, a legacy ASP.NET Web Application running in Mono in Linux (Nginx Web Server) is number 356 in the list.

Note: Test number (Round 18) dated 07/09/2019

From these results mentioned above, you can see ASP.NET Core applications are significantly faster than legacy ASP.NET applications.

Cloud and DevOps

.NET Core is built with Cloud in mind. Till .NET Core 3.0, it only supported server-side programming paradigms, such as ASP.NET Core, ASP.NET Core Web API, etc. From day one, .NET Core Web applications were equipped with the tools and support to run on Docker containers. As we all know, to run an application in the cloud today is to run in a container. At least in the Linux world, as it is extremely cost-efficient and easy to scale.

How does this help? Well, managing a container-based application is extremely easy. Plus, creating a container from scratch and starting it to serve web requests happens within a few seconds, compared to traditional virtual machines (where we usually host legacy ASP.NET applications), which takes double-digit minutes. Hence, scaling out a container-based application is instantaneous and easy to manage scale as well as cost.

Apart from that, the cloud providers, as well as in-house data centers, can manage containers with ease using open-source container orchestration engines like Kubernetes or using the offerings by the cloud providers. For example, AWS provides both managed Kubernetes (Amazon Elastic Kubernetes Service (Amazon EKS)) as well as its own managed container service (Elastic Container Service (ECS)). Similarly, Azure provides both managed Kubernetes (Azure Kubernetes Service (AKS)) as well as its own managed container service (Azure Service Fabric).

Support for Modern Architecture

A modern architecture that is extremely popular is the microservices architecture. And .NET Core is ideal for building microservices. To dig more into it, let us first define what is a microservices architecture.

Microservices architecture is a pattern for building highly scalable services. It is the opposite of a monolithic architecture. In a monolithic architecture, an entire application is part of the same .NET Project/Solution and is available in the same source repository as well as a single hosted service. However, microservices architecture is the complete opposite. In a microservices architecture, every service is a single responsibility service and provides only one functionality in a business domain.

For example, in very simplistic terms, in an e-commerce system, a service managing only user logging, can be considered as a microservice. Whereas in a monolithic architecture, the entire e-commerce system will be exposed as a single service (or a couple of services, in most cases a single one).

For us, the biggest feature a microservice provides is the ease of deployment. Because microservices are meant to be independently deployable, of course, we have to design it to do that. Apart from that, microservices are loosely coupled, highly maintainable (due to their smaller size and single responsibility), and highly testable.

Since microservices are smaller in size, a single application is usually broken down into multiple microservices. Due to this, these are usually deployed in Docker containers and managed through a container orchestration engine. The reason for going with containers is that the services can be scaled independently of each other and on-demand based on request volume.

Given the support for Docker containers from day one, ASP.NET Core becomes an obvious choice for building microservices architecture. And in Visual Studio 2019, the ASP.NET Core application builder comes with support for Docker in the IDE itself. And creates all the required scaffolding during the project creation. Which makes it really easy to get started with a containerized microservice in ASP.NET Core.

Security Advantage

There are two types of security to consider. One is how vulnerable .NET Core itself. On that front, since .NET Core applications can run on Linux servers, they inherently get all the security advantages of running on a Linux server.

The second aspect is securing an ASP.NET Core Web Application. Well for that, there are plenty of options available, including Custom Authentication mechanism (using Authentication Handlers), JWT (JSON Web Token) Authentication, and OAuth. And finally, third-party authentication providers, like Google, Facebook, Etc. All these can be built with a few lines of code and existing Nuget packages.

Now that we understand why we should migrate to .NET Core, let us take a look into the ways to handle the .NET Framework to .NET Core migration.

Legacy .NET Framework to .NET Core Migration

In our opinion, there are two approaches to migration. The first approach is to use tools to convert .NET Framework to .NET Core. The second approach is to re-write with porting (now don’t get anxious, we will explain in detail in which scenario it makes sense).

Convert .NET Framework to .NET Core

In this case, we will follow the following steps, one thing to keep in mind, there is no magic here, there will be some rewrites and reworks based on project complexity:

  1. Re-target all projects to .NET Framework version 4.8 (the latest version). This will take us close to .NET Core in the sense of API portability.
  2. Use .NET Portability Analyzer to find out API in our current legacy .NET Framework project, which is not supported in .NET Core. At this point, keep the list of alternative APIs in .NET Core, so once we move our code over to .NET Core, we will use the new set of APIs.
  3. Create a new .NET Core project based on the existing project type; it will either be a Class Library, Console, or Web API. NOTE: If we are moving over a WebForms project, we will have to re-write the UI layer in MVC Razor syntax, though we could reuse all the HTML, JavaScript, and CSS.
  4. Copy all the files into the new project and add the necessary NuGet packages. (In our experience, we have not seen any gap in NuGet packages between legacy .NET Framework and .NET Core.
  5. Rework wherever needed due to the non-availability of a legacy API. Here, we will use the list of alternative API’s from Step 2.

Re-write with Porting

We would suggest this approach if you have a monolithic architecture. In that case, we can take one domain-specific functionality at a time and convert it into a microservice. During this process, we can take the existing code and follow the first approach and port only part of the monolithic service into a microservice built-in .NET Core. And in the process, we can also ensure we are using modern design practices like SOLID principles if it is not already followed.

With this approach of converting one responsibility of a monolithic service at a time into a .NET Core microservice, we will de-risk the entire process significantly. We will also have a significantly low-cost testing and deployment strategy.

Should You Convert?

Given the advantages provided by .NET Core over legacy .NET Framework, it’s a no-brainer to migrate to .NET Core. But as with every upgrade project, we have to be mindful of the cost. In our experience, an experienced software developer team following a step-by-step breakdown of an existing application and porting it in pieces is very cost-effective and less risky approach.

Converting a big application in one go isn’t impossible, but it is difficult. In addition, we’re likely incur technical debt due to the pressure of delivery.

Whereas if we break an application into smaller microservices and/or components, we have a better chance of success. And as and when we go with the conversion, we can follow the current design principle and best practices along the way.

Ashutosh Kumar

Ashutosh is a Senior Technical Architect at Taazaa. He has more than 15 years of experience in .Net Technology, and enjoys learning new technologies in order to provide fresh solutions for our clients.