Common Pitfalls Implementing ASP.NET Core Apps
Special thanks to Matthew Wilkin for kindly helping to peer review this article.
Over recent years, Microsoft has touted the ASP.NET Core framework as the future of ASP.NET. ASP.NET Core is of interest to web professionals on the Microsoft stack. If you’re running web applications on Windows and want to make the upgrade, what are the gotchas? Is the transition to the latest and greatest paved and smooth?
In this take, I’d like to delve into common pitfalls you may run into when you upgrade to ASP.NET Core — assuming you’re eager to make the switch. As developers, it’s always exciting to get your hands on shiny new tools that make your life easier.
For this article, I’ll use “ASP.NET classic” to refer to the legacy ASP.NET framework. I’ll use “ASP.NET Core” to refer to the standard framework Microsoft makes available to ASP.NET developers.
Microsoft had made positive strides towards making ASP.NET Core cross-platform. The new tooling runs on Linux, macOS, and Windows. It may be comforting to know your back-end code now runs anywhere. This frees you, the developer, from having to code on a Windows box. The new tooling available caters to your personal preferences.
Coming from ASP.NET classic, a valid assumption is that you’re on the Windows platform. To you, going cross-platform may not be relevant right at this moment. Up until now, ASP.NET classic has been a first-class citizen and exclusive to Windows. This tight integration with Windows and IIS, of course, is both a blessing and a curse.
So, what does ASP.NET Core mean for developers on Windows Server and IIS? Now that ASP.NET Core is cross-platform, what are the implications?
One natural consequence from decoupling from IIS is an explosion of dependencies. ASP.NET Core runs on Kestrel, a separate process that responds to HTTP requests. This means the framework leans on a bunch of dependencies where IIS once stood. These dependencies are basic NuGet packages you can download from the internet.
With ASP.NET Core, NuGet is the canonical way to get dependencies. In fact, most of the tooling is now a decoupled NuGet package you can add as a dependency. The tooling you install on your local dev box is only a shim. The intent is to get your app to be self-contained with all its dependencies. The majority of ASP.NET Core comes in modular NuGet packages you plug and play.
This is a fresh take on the old monolithic framework tight-coupled to IIS. ASP.NET Core is cross-platform and means it. What was once leaning on colossal IIS and Windows installs is now reduced to a NuGet package. This makes the framework nimble and more flexible. It’s easier to change a small NuGet dependency than installing modules on your web server.
But, as often is the case, there are a few gotchas:
- On the local dev machine, the team saw a NuGet packages folder with 1GB worth of dependencies.
- If your build server is behind a firewall, you upload each tiny dependency — a tedious process.
- By default, the tooling brings in meta-packages, which may have extraneous dependencies.
One downside with meta-packages is that, if you’re on Windows, you’ll see bizarre dependencies like:
It’s not common to include a Linux and macOS runtime as a dependency, given that you only expect to run this on Windows. Yet, if you want to pass the build, it is necessary. There is a way to reduce your dependency footprint, but not very practical in an enterprise setting. Few professionals have the time to tweak a gig worth of tiny little dependencies. The hope is the tooling will do better and normalize dependencies for you in the future.
There are certain challenges to get a successful build on your build server. My team and I felt like this was going down a roller coaster ride we hadn’t prepared for. An endearing codename within the team for ASP.NET Core was “Fat Lady”, because of all the dependencies.
This leaves us with a question. How do you get this sizable framework into a web server?
If your team has working deployments on ASP.NET classic, you’ll see that a lot has changed. ASP.NET Core uses a new set of tools for deployments.
Coming from ASP.NET classic, the hope was that deployments were reusable. In automation, there’s a set of scripts you use to get changes onto a web server. The scripts that run in automation are often called a deployment pipeline. In Windows, this means PowerShell scripts to ensure reliable and predictable deployments. Having reusable pipelines is one way to ensure consistent deployments.
The ASP.NET Core tooling uses a separate set of command line tools to create a deployment package. There are nuances with this that will force you to create a new deployment pipeline. For example, MS Build was the CLI tool of choice for ASP.NET classic deployments. With ASP.NET Core, there’s a
dotnet publish command to create a package. The subtle differences between the two cast the team into a new pipeline. Unfortunately, much of the existing automation was not reusable.
The automation for ASP.NET classic made assumptions that were no longer valid. For us, the goal was to gather together the bare necessities for a good deployment. This took an all-hands-on-deck approach to flesh out all the necessary details.
Microsoft in recent releases of ASP.NET Core has added support for MS Build. Given the time frame, we were a bit too early for this announcement. One idea is review deployment requirements before you delve into ASP.NET Core. Investigate which part of your existing pipeline is reusable.
The situation on IIS is much the same: there are plenty of changes. Windows Server and IIS need a new module for ASP.NET Core apps to work. A good solution is to isolate ASP.NET Core apps from classic legacy apps. The HTTP module necessary may have a negative impact on ASP.NET classic. On a live server, make sure you apply latest patches and have time to sort out any issues while the server is down. There are guides to get you started with setting up ASP.NET Core in IIS.
Now that the Fat Lady (ASP.NET Core) is happy and running, what other pitfalls are there?
Managed Vs Unmanaged Code
With automation, one common pitfall is to set the application pool to managed code. In ASP.NET Core, this is one piece of configuration you cannot reuse. One issue that kept cropping up during deployments was setting the app pool to the wrong mode. If you do so, you’ll see a process failure with a 502 error.
What may cause confusion is that ASP.NET Core runs on top of the standard framework. ASP.NET classic, for example, runs on the classic framework and in managed code. In ASP.NET Core and IIS, the process that hosts your web application does not run on any framework. ASP.NET Core runs in a separate process, decoupled from the standard framework.
Make sure you set the application pool to unmanaged code. When you double click the app pool, it should look like this:
IIS runs ASP.NET Core apps as an external process through a reverse proxy. This is because it no longer has this tight integration with IIS and is cross-platform. The web server uses an HTTP module to track the process. ASP.NET Core has all the middleware necessary to send the HTTP response, it no longer depends on IIS.
ASP.NET Classic Dependencies
Any dependency that’s not on ASP.NET Core will have consequences. Inside your list of dependencies, you may find one that depends on ASP.NET classic. Anything that depends on ASP.NET classic needs the entire legacy framework for it to work.
Microsoft has made great strides towards making the transition seamless. For external popular dependencies, you’ll often find packages that work with ASP.NET Core. The ASP.NET Core framework has many of the same packages already familiar to you. This makes the upgrade a lot more practical and attainable.
For example, ASP.NET Core has packages for Entity Framework, Dapper, and ADO.NET. These packages are like their counterparts in ASP.NET classic.
With in-house dependencies, the situation is quite different. If the dependency doesn’t have an ASP.NET Core port, you’ll need ASP.NET classic. ASP.NET Core and ASP.NET classic are exclusive. This is often a blind spot for teams looking to move to the shiny new framework.
A good approach is to assess your list of dependencies and figure out what depends on what. Figure out your hierarchy of dependencies and drill into dependencies of a dependency. This way you know ahead of time what to expect. The idea is to cut surprises that behave in unpredictable ways during deployments.
If you still need ASP.NET classic because of dependencies, it is possible to run ASP.NET Core on the legacy framework. But, this adds more risk to your deployments, such as dealing with authentication cookies.
One gotcha is that the default behavior you expect from dependencies will change. When wiring up ASP.NET Core dependencies, make sure you’re not making any assumptions. For example, the authentication cookie has a different set of default settings. ASP.NET Core is in some cases a complete rewrite, so a safe assumption is that default behaviors will change. Don’t make any of the same assumptions you made in ASP.NET classic. Test and verify all your dependencies.
Overall, the new framework you get with ASP.NET Core is pretty slick. As a developer, you’ll find a better coding experience. The tooling streamlines the framework and it’s accessible from outside Visual Studio. Decoupling from Windows and IIS is a step in the right direction.
With ASP.NET Core, you’ll find a leaner framework with less cruft. I find the CLI tooling refreshing and very effective. For example, to run all unit tests, do
dotnet test and get instant feedback in less than a second. There’s no need for clicking and getting frustrated in Visual Studio to get feedback. When you have a bunch of tests, the feedback in Visual Studio is often slow. The general coding experience is one that comes from a minimalist modern framework.
The ASP.NET Core MVC framework comes with improvements. Web API and MVC are now a single framework. You inherit from a single controller to use both. There’s no confusion in determining which one to use, which makes it more effective.
As with all new things, there are consequences to diving right in headlong. I hope this article points out common pitfalls you’ll want at least to consider. Best to know what you’re getting yourself into before embarking on this new adventure.