Software Whisperers like myself live for those teaching moments in which we truly open someone’s eyes to new possibilities and completely upturn that person’s worldview. My favorite experiences are when I’ve blown the minds of senior C# developers with these four little words: “.NET runs on Linux.”
Suddenly, the light bulbs click on and the motors start to whir. The gears creak to life and the realm of possibilities is suddenly four times wider! .NET is one of the most productive platforms for enterprise software development. Linux runs the web. Why not .NET … on Linux?
If your .NET programs are cross-platform, you can run them anywhere Linux can run, however, .NET can run in many more places than just Linux. Unity runs .NET on game consoles (on the native game console operating systems), Xamarin runs .NET on mobile devices (on iOS and Android), and .NET Micro Framework runs it on microcontrollers (on bare metal hardware).
What does this mean for creators of enterprise software? If your software can run cross-platform, you can deploy your .NET applications almost anywhere, giving you greater control of audience targeting and reach. It means you can retarget your legacy applications onto the newer .NET standards, and launch them in any cloud provider using modern deployment strategies and orchestration technologies. The best part is moving away from managing operating systems and, instead, deploying .NET applications into Platform as a Service (PaaS) providers, reducing time to market and improving the flexibility and expandability of applications.
What makes .NET cross-platform?
For businesses building applications, productivity and flexibility are a winning combination. .NET is not only powerful, but inherently cross-platform and has been since its inception. Here’s why:
- .NET binaries run on all platforms because they are a standardized according to Microsoft’s Common Language Infrastructure (CLI, ECMA-335).
- .NET compilers (csc) and build script (msbuild) are identical for all platforms and are open source (Roslyn, MSBuild).
- Mono enables the full .NET Framework to run on Linux platforms with a high level of compatibility, as well as different processor platforms like ARM (Raspberry Pi) or PPC (IBM OpenPOWER).
- .NET Core runs on Windows and Linux, and Microsoft is very vocal about supporting both, as its release announcement for the most recent .NET Core 2.1 proves.
- PowerShell Core lets you build automation cross-platform or run more advanced scripting for DevOps, including psake, which is part of the standard Headspring development stack.
Within the current tooling landscape, .NET software development teams have more choices than ever on where to build, test, and deploy their code.
Preparing your software to run cross-platform
There are two main requirements for making your software platform-agnostic:
- Don’t use any APIs that are not implemented across other platforms (including Base Class Library methods that are not implemented outside of Windows and PInvoke calls to Windows-only libraries).
- Properly treat file and folder paths so that platform-specific path separators (i.e., “\” and “/”) are not used explicitly.
Retargeting your project libraries to .NET Standard 2.0 (netstandard20) will resolve most of the concerns with missing APIs. However, it will also prevent you from adding runtime checks for the operating system and choosing any alternative implementations. Targeting the .NET 4.6+ or 4.7+ framework will allow you to still use Windows-specific libraries on Windows and switch to cross-platform libraries on other operating systems.
We also recommend installing the .NET Portability Analyzer extension into Visual Studio. It allows you to select the platforms to target (across mobile, Mono, .NET Core, .NET Standard, etc.) and run an analysis to determine which API’s should not be called. This is quite useful for brownfield projects, to get a general idea of all the parts of code that will need to be touched in order to prepare the software to run cross-platform.
Most software products need to run automatically at system startup. In Windows, this is handled through services and the Windows Service Control Manager. In modern Linux distributions, this is handled through systemd. In both cases, the software needs to think about how the entry point (Main) works, how arguments are handled, how the service is installed and removed, and how the service responds to operating system events (start, stop, restart). The handy TopShelf library is a solid starting point for your application. For .NET 4.5+, it will allow you to install and uninstall the service from the command line. For .NET Core and Mono, you have to manually install the service, since certain operating system-specific calls are not available. To run on Linux with the same binaries, we recommend using the TopShelf.Linux library to turn off Windows specific calls and avoid fatal errors at startup.
All modern unit testing frameworks already run cross-platform, even Microsoft MSTest V2 now runs on Linux and Mac! If you port your unit tests to run on the most recent version of your favorite testing framework (such as Fixie 2.0), the tests can be run almost anywhere.
Platform as a Service (PaaS) Options
Sometimes you just need to expose port 443 to the world. Sometimes you just want a simple API endpoint and you don’t have any firm relational database vendor requirements. In these cases, you want the simplicity of a lightweight PaaS provider that gives you only a few levers to pull to adjust the load your application can manage. The major benefits of these services are cost and simplicity.
Other times, you need to host huge, complex applications that would be completely unmanageable on a lightweight provider. In these cases, you need to ensure the hosting vendor provides tools and APIs for managing the infrastructure. Let’s look at four PaaS options:
Heroku has been around for 11 years providing an easy-to-understand platform, built around compute units called “dynos” and simple data size plans. Hosting your .NET Core applications on Heroku relies on their support for Docker containers. The instructions walk you through how to build and deploy an application using the Microsoft/aspnetcore Docker image based on Debian Linux.
Cloud Foundry Application Runtime is part of the open source Cloud Foundry platform that allows you (or other vendors) to host and manage applications on bare metal hardware or cloud providers. Cloud Foundry uses “build packs” to provide a Linux environment for your application to run. A .NET Core build pack is available that should be compatible with any vendor that provides Cloud Foundry hosting. Cloud Foundry also provides serverless database hosting. IBM Bluemix (now called IBM Cloud), Pivotal and Amazon Web Services all provide Cloud Foundry hosting options.
Microsoft Azure Service Fabric provides both Windows and Linux-based hosting for microservice applications. If you can use the Service Fabric APIs in your application, you can take advantage of the capabilities provided by Service Fabric to monitor, deploy, scale, and maintain your applications. The Linux hosting options for Service Fabric are less expensive than comparable Windows hosting options.
Docker is a container technology on Windows and Linux that allows you to run your applications in a lightweight Windows or Linux environment. Many hosting providers offer container-based application hosting, such as Azure’s Managed Container Service and Amazon’s Elastic Container Service. If you can run your application in a Docker container, you can run it just about anywhere, however, Linux container hosting is more readily available and usually less expensive.
Each PaaS solution has its own benefits and detriments, depending on your budget and the nature of your application. However, even with all these choices to make, you can rest assured that your .NET application will run identically on all the different platforms and providers.
Onwards and upwards!
The journey to cross-platform .NET applications is a challenging but rewarding one. While running on Linux is useful, the real business value comes from running on a PaaS or hosting provider. Upcoming articles will explore different scenarios for modernizing .NET applications to make them run identically on Windows and Linux, and talk about how to deploy them onto various platforms and cloud services. Today’s technology makes it possible, but it takes extra effort in the software development to make it actually work. In the end, you’ll be able to see many more of the ways you can get your .NET applications in front of your customers and maintain them effectively.
.NET Micro Framework: https://netmf.github.io/
NET Core 2.1: https://blogs.msdn.microsoft.com/dotnet/2018/05/30/announcing-net-core-2-1/
Powershell Core: https://github.com/PowerShell/PowerShell
.NET Portability Analyzer: https://github.com/Microsoft/dotnet-apiport/blob/master/docs/VSExtension/README.md
Windows Service Control Manager: https://msdn.microsoft.com/en-us/library/windows/desktop/ms685150(v=vs.85).aspx
Microsoft MSTest V2: https://github.com/Microsoft/testfx
Fixie 2.0: http://fixie.github.io/
Cloud Foundry Application Runtime: https://www.cloudfoundry.org/application-runtime/
Microsoft Azure Service Fabric: https://azure.microsoft.com/en-us/services/service-fabric/