Running .NET Core Web host and Kestrel server on Xamarin Forms apps
In one of my previous Xamarin projects, I needed a simple Web server running on the phone. I used EmbedIo Web server package and it worked like a charm.
For a new Xamarin project I am working on, I need a Web server again but this time I was wondering if I can use .NET Core Web hosting and employ Kestrel server. These components constitute the base infrastructure for .NET Core applications and frameworks such as Web API, MVC, SignalR etc. Hence ability to get them running on Xamarin will open up the doors to running .NET Core applications and frameworks on Xamarin apps out-of-the box.
So I started experimenting…
Analysis
The first task is to create a minimal ASP NET Core app source code, copy this code into a .NET standard 2.0 library and build it so that Xamarin can consume it.
Xamarin currently supports .NET standard 2.0 and 2.1 and it is not compatible with .NET5 ASP NET Core. So I use Servers/Kestrel/samples/ PlaintextApp/Startup.cs file from aspnetcore github repo v2.2.7 as my minimal code.
Startup.cs file has references to Microsoft.AspNetCore.Xxx and Microsoft.Extensions.Xxx assemblies. To resolve dependencies I added the following NuGet packages:
The library builds fine and it is ready to be consumed by a Xamarin app. So I created a minimal Xamarin app (Android and iOS) and added this library as a reference. I called the Main from App.cs in a separate thread so that both Xamarin and WebHost can run side by side. But unfortunately WebHost throws an exception: “Operation is not supported on this platform.”.
Stack trace indicates that the issue is originated from Console.KeyPress.
So some debugging is needed to find the exact root cause of this exception. Debugging requires the source code of the two NuGet packages mentioned above. Here are the links to source code on GitHub:
There are multiple ways to debug these:
- By injecting the Xamarin test app to the existing repos from a separate folder, build them and debug. Follow the instructions provided by these repos to build them.
- Import individual library projects from these repos and integrate them to the Xamarin test app.
- Use a decompiler tool such as ILSpy to generate PDB files or even projects from the existing DLLs.
- ,,,
I don’t want to go into further details here but I identified four issues as result of code debugging:
- Console lifetime issue: The file is ConsoleLifetime.cs, on line 61:
Console.CancelKeyPress += OnCancelKeyPress;
It seems CancelKeyPress event is not supported on Xamarin and throw an exception. This issue will be hit when using a generic host. - Web host extension console issue: The file is WebHostExtensions.cs, on line 159:
Console.CancelKeyPress += (sender, eventArgs) =>
Again CancelKeyPress event issue as above. - Configuration issue: .NET Core Web applications require some configurations parameters from Configuration package such as:
ContentRootPath = configuration[WebHostDefaults.ContentRootKey];
Xamarin has no such package so all references to these parameters return null. Null parameters will throw exceptions in some code. - InplaceStringBuilder issue: Microsoft.Net.Http.Headers v2.2.0 from AspNetCore assembly references InplaceStringBuilder from Microsoft.Extensions.Primitives assembly to format date and time. Unfortunately InplaceStringBuilder has been removed from Microsoft.Extensions.Primitives in v5.0. Hence the call ends up with run time error.
Resolving of Issues
- Console lifetime issue: Cancel key press (CTRL-C) is used to tear down the Web host and server in a controlled fashion in NET Core environment. This is irrelevant in Xamarin as it has no visible console and we are not starting the Web app from console. Invoking CTRL-C handler with Xamarin app going down event is also irrelevant as detecting this event is not always possible. We can safely ignore this event as Xamarin will take down the Web host and server when terminating. So commenting out this event handler is OK.
Instead of modifying the existing code:
- clone the ConsoleLifetime.cs as ConsoleLifetimePatch.cs
- comment out the event code
- call services.AddSingleton<IHostLifetime, ConsoleLifetimePatch>(); from ConfigureServices
DotNet code registers the original ConsoleLifetime service at the early stages of initialization. Hence by registering the patched one in ConfigureServices, we are overriding the original service and ConsoleLifetimePatch would be the active one. - Web host extension console issue: The problematic code is called from C# extension function RunAsync. Again instead of modifying the original code:
- clone WebHostExtensions.cs as WebHostExtensionsPatch.cs
- comment out the event code
- Add a new C# extension method RunPatchedAsync
- New extension method will call the function that contains the commented code
This change requires user code calling RunPartchedAsync instead of RunAsync to start the Web host. - Configuration issue: I have already created a NuGet package Xamarinme.Configuration that provides .NET Core style configuration feature to Xamarin apps. So using this NuGet and providing the required config parameters in appsettings.json file resolved the configuration issue.
- InplaceStringBuilder issue: There is no easy trick for this issue. The following steps have been taken to resolve it:
- Microsoft.Extension.Primitives project has been cloned
- The missing InplaceStringBuilder.cs file has been added from v2.2.0
- The assembly version is set to v5.9 so that it will override v5.0 of Microsoft.Extension.Primitives assembly.
Result
After resolving the issues, .NET Core WebHost and Kestrel server run successfully on Android:
and on iPhone:
I have created a NuGet package that contains all these patches (source code is here) that can be used by Xamarin applications. A sample demo app is also provided on my GitHub repo.