In this post I want to describe a high-level strategy for migrating legacy ASP.NET MVC applications to ASP.NET Core. In one of the projects I work on there is a lot of code that was written in ASP.NET MVC, and uses Entity Framework.
Now, in one sense, there's nothing wrong with this - the "classic" ASP.NET MVC framework still works just fine. But there are a growing number of reasons to want to get away from the legacy platform and onto ASP.NET Core. Here's a few of my top reasons:
- Classic ASP.NET runs on Windows only, limiting your options if you want to containerize your services
- ASP.NET Core is much faster
- Classic ASP.NET is now end-of-life, meaning it doesn't benefit from many other innovations, and blocks you from adopting several newer technologies (like Entity Framework Core or gRPC)
Of course, for many developers, moving to a new technology stack presents an opportunity to completely rewrite your service. This allows you to clear off a bunch of technical debt in one go, and end up with a faster, more maintainable service with a better API design.
However, it's well known that software rewrites often tend to take a lot longer than expected to complete and can result in significant regressions. That's because legacy services tend to become bloated with too many responsibilities (something that a good microservices architecture hopefully avoids), and also because legacy services often don't come with a comprehensive suite of integration tests that could serve as an acceptance test for the rewritten service.
Keep as much as possible
The alternative is to keep as much of your existing code as possible. The main challenge we faced here was the fact that you're not just moving from ASP.NET MVC to ASP.NET Core MVC, but you also need to move from Entity Framework to Entity Framework Core at the same time.
However, the good news is that Entity Framework 6.4 bridges the gap between the worlds of legacy .NET Framework and .NET Core. It dual targets both .NET 4.5 and .NET Standard 2.1. This greatly reduces the amount of code that needs to change as part of the migration (although of course you may still want to upgrade to EF Core at a later date).
A recipe for migrating
Here's a simple process that we're following to slowly turn some legacy ASP.NET MVC services into .NET Core applications.
First, upgrade to use Entity Framework 6.4. This is relatively straightforward, and a lot less effort than rewriting to use EF Core which does have a different feature set.
Second, identify any NuGet dependencies that don't target .NET Standard, and find alternatives. We found that most of our dependencies that targeted .NET Framework could be upgraded to newer versions that supported .NET Standard.
But there were a few tricky things like libraries for PDF generation or image processing. These have potential to be blockers to getting to a point where you can run cross-platform, so you may need to consider breaking out any legacy code that absolutely cannot be ported into a separate microservice that still runs on .NET 472.
Of course, many of your dependencies may be your own shared libraries. We converted these to target .NET Standard 2.0, meaning that newer .NET Core services and legacy .NET Framework services could both take dependencies on them.
Third, move all business logic and database access code out into library classes that dual target .NET 472 and .NET Standard 2.1 (this is needed because of EF 6.4 which doesn't target .NET Standard 2.0). This requires using thin controllers rather than thick controllers, where the controller's only job is to call (maybe via a mediator) into a request handler and then convert its response into the appropriate HTTP response object.
Fourth, once you've reached this point, you can create a new ASP.NET Core 3.1 application, and reference all your business logic libraries. Then you need to recreate all your controllers and routes, as well as set up all your middleware (e.g. authentication, swagger) again. This part is a rewrite, but it's a much smaller scoped rewrite than would have been necessary if all the database access code had to change as well.
Thanks to EF 6.4 and the ability to dual target libraries, it's possible to migrate from ASP.NET MVC to ASP.NET Core MVC without having to completely rewrite all your database access code.
Let me know in the comments if you've done a similar thing or if you have any other tips for making the migration go smoothly.