Working Cross-Platform with .NET Core
One of the key benefits of .NET Core is its ability to work on both Windows and Linux/Mac. This article aims to help you do that most effectively and take advantage of the flexibility this provides.
Developing for Cross-Platform Use
When you start developing .NET Core apps and code that need to run cross platform, you have to deal with the more complex set of standards in the .NET world that have arisen since Core came on the scene.
Essentially:
- Shared libraries should target .NET Standard if they are to be used cross-platform. Increasing version numbers of .NET Standard allow a wider range of APIs. You should try and build your library to target the lowest version of .NET Standard you can manage so that it can be used by the widest range of clients. This will generally be the highest version of .NET Standard used by any of the Nuget packages your library in turn references. The bonus here is that your shared libraries can also be used by .NET Framework code.
- Applications should target .NET Core. Unlike shared libraries, your application should target the most recent stable version of .NET Core, as no other code will need to depend on it and that gives you the widest range of possible things you can require in terms of Nuget packages and shared libraries.
Another complexity that is a feature of .NET Core 2.0 and above is that it is possible for a .NET Core application to target (some) .NET Framework binaries and Nuget packages. But if you do this, those binaries/packages won’t run on Linux/Mac, so don’t do it if you want your application to be cross platform.
Self-Contained or Framework Dependent
You can build .NET Core apps in two ways. If you start with a Visual Studio template for .NET Core by default you will be building a framework dependent app. Such an application when published, can run on any platform, so long as the machine has the .NET Core runtime installed on it.
For self-contained apps, you specify one or more platforms in the .csproj file and the publish output contains all libraries and the dotnet runtime required. The self-contained app therefore has no dependencies and can be run on the specified platform only without any other installations.
The platforms are specified by adding a line in the .csproj file as below:
<PropertyGroup>
<RuntimeIdentifiers>win10-x64;osx.10.11-x64</RuntimeIdentifiers>
</PropertyGroup>
The current list of available platforms can be found here and here.
You can specify multiple platforms, but then for any given publish action, you have to choose which one you’re going to build, either via the -r switch on the dotnet publish command, or in Visual Studio by choosing the Target Runtime in the Settings dialog for the publish profile you’re using.
To learn more about these two options, go here.
Cross-Platform Coding Practices
Generally, the beauty of .NET Core is that if you build using it, your code will simply work on all the supported platforms. There are a couple of gotchas to beware of however where you can inadvertently make your code platform specific if you don’t follow the right rules.
- Where you do file IO and need to use file system paths, Core isn’t clever enough to automatically translate the path separators from the Windows ‘\’ to the Linux ‘/’ (or vice versa). The way to code around this is to use the Path class, either using the Path.Combine method to build your path, or using the Path.DirectorySeparatorChar constant to get the separator character itself.
- Both URLs and file paths are case-sensitive on Linux-based systems, and case-insensitive on Windows ones. You need to be aware of this when constructing file paths and web links to static files. Also, when using ASP.NET Core routing, be careful about mapping URL elements to controller or action names, as these will usually have an initial capital. Probably the best route is to use the ActionNameAttribute to make action names lower case, and avoid mapping URL elements to controller names.
You might also like to look at this series of blog posts which has some more specific issues.
What Cross-Platform Enables
Obviously, building a cross platform .NET Core application opens up the possibilities of interacting with the whole world of Linux, and there are many use cases you can imagine, the most obvious of all being to take advantage of cheaper Linux-based server infrastructure. Here are some of the commoner other ones.
Run the Back End on Front End Developers’ Macs
If you are a back end .NET developer used to working on Windows, these days you will find the vast majority of front end devs will be working on their Macs. With .NET Core it’s now easy for them to install your back end locally on their machines. I’ve found the following setup works very well in this context where the front-end is on a JS framework and talks to the back-end via an API.
- Have separate repositories for front-end and back-end.
- On the back-end Windows machine, install Node and NPM together with whatever the front-enders are using to build the compiled front-end files.
- Whenever you need to update or create the front end on the Windows machine, run the build process and copy the compiled files into wwwroot in your ASP.Net Core project.
- Use the MapSpaFallbackRoute method in ASP.NET Core 2.0 or the UseSpa method in ASP.NET Core 2.1 (for details on this one see this) to make this work with ASP.NET Core.
- On the front-end Macs, install the.NET Core SDK, and download the back-end repository.
- To start the back-end API, in a shell, go to the directory containing the .csproj file in the repository download, and run ‘dotnet run’. This will start up the back-end in the Kestrel web server: the output will tell you what port on localhost this is running on.
- Have a configuration switch in the front-end so that in development mode, it will look for the back end APIs on the localhost URL (with appropriate port), and in production, it will look for the APIs on the domain on which it’s running.
- If you need, e.g., SQL Server, on the front-end machines, you can set up Docker and run it in Docker.
Use Docker Better
While it’s possible to run Windows applications on Linux/Mac under Docker, it also requires installation of a VM such as VirtualBox. .NET Core applications can run directly on the Linux/Mac underlying OS so this isn’t needed for them, making .NET Core an ideal platform for creating containerized applications.
Use for Serverless
.NET Core is now supported on AWS Lambda, Azure Functions, and Azure Service Fabric. You can build ASP.NET Core applications too, if you’re careful to optimize them for rapid startup.