Before we start with why we should migrate from legacy .NET Framework to .NET Core, 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.
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, 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 filled the gap due to which desktop applications could never adopt .NET Core earlier. Pre .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.
.NET Core is getting adopted very fast, and for obvious reasons, some of which we will discuss in detail later in the blog. According to StackOverflow survey conducted for 2019 .NET Core tops the most loved in Framework, Libraries, and Tools category. Even exceeding PyTorch which is an open-source Machine Learning framework for python.
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 with legacy .NET Framework.
The main drawbacks with .NET Framework
.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 not be any innovation in the .NET Framework anymore. Tough Microsoft mentions that they will continue to support the .NET Framework, but 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. But developers are more interested in joining a .NET Core developer role rather than .NET Framework developer role.
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 2,394 Jobs, whereas for .NET Framework there are 2,398 Jobs. Both the results are very close. Meaning 50% of jobs in demand today are for .NET Core. This also shows the supply and demand for both legacy .NET Framework and .NET Core are almost equal at this point.
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 of running a Legacy ASP.NET Web applications is in an IIS Server running in 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 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 in the Linux server and realize the savings.
Another important, but not obvious savings for ASP.NET Core application comes by 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 container, and we can have multiple containers running the same application or different applications inside a single Linux server, intern 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.
Given the fact that Microsoft has a complete focus on .NET Core and does not intend to make any feature change to .NET Framework 4.8, there is always a risk of security running in .NET Framework versions. Tough Microsoft clearly mentions that they will continue to provide security patches to .NET Framework 4.8, but .NET Core always has the advantage given its open-source nature and being actively developed.
Advantages of .NET Core
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 Linux box. So 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 in 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 of the list.
For JSON serialization, ASP.NET Core Web Application running on Kestrel Web Server on Linux is in number 11 in the list. Similarly, a legacy ASP.NET Web Application running in Mono in Linux (Nginx Web Server) is in number 356 in the list.
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 was only supporting server-side programming paradigm, such as ASP.NET Core, ASP.NET Core Web API, etc. And from day one, .NET Core Web applications were equipped with the tools and support to run on docker containers. And 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 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 in 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:
One of the modern architecture that is becoming extremely famous is a microservices architecture. And .NET Core makes it ideal to build 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 providing 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 smaller size and single responsibility) and highly testable.
Since microservices are smaller in size, hence a single application is usually broken down into multiple microservices. And 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 container 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.
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 security advantages of running in 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, OAuth. And finally, third-party authentication providers, like Google, Facebook, Etc. And all these can be built with few lines of code and existing Nuget packages.
Now that we understand the advantages of jumping into .NET Core boat, let us take a look into the ways to migrate an existing legacy .NET Framework application into .NET Core application.
Legacy .NET Framework to.NET Core Conversion
In our opinion, there are two approaches to conversion. The first approach is to use tools to port from the .NET Framework to .NET Core. And 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).
First Approach of porting:
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:
● Firstly, we will re-target all projects to .NET Framework version 4.8 (the latest version). This will take us close to .NET Core in a sense of API portability.
● Secondly, we will 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 API in .NET Core, so once we move our code over to .NET Core, we will use the new set of API.
● Fourthly, copy all the files into the new project, and add the necessary NuGet packages into the project (in our experience, we have not seen any gap in NuGet packages between legacy .NET Framework and .NET Core).
● Finally, rework wherever needed due to non-availability of legacy API (here we will use the list of alternative API’s from step number 2).
Microsoft’s approach for porting a legacy .NET Framework application to .NET Core is available here.
Second Approach of re-write with porting:
We would suggest this approach if you have a monolithic service. In that case, we can take one domain-specific functionality at a time and convert it into a microservice. And 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 as time into a .NET Core microservice, we will derisk the entire process significantly. And also will have significantly low-cost testing and deployment strategy.
Given the advantages provided by .NET Core over legacy .NET Framework, its a no brainer to upgrade. But as with every upgrade project, we have to be mindful of the cost. In our experience, with an experienced software developer team and following a step by step breakdown of an existing application and porting into pieces become very cost-effective as well as less risky.
If you take a big application and want to convert in one go, well it is not impossible, but it is hard. And also chances of paying some of the technical debt is possible 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.