To run an existing ASP.NET app on Unix/Linux, you can proceed in different ways: rewrite it from scratch using the new ASP.NET 5 framework or fall back upon Mono, a free and open source .NET Framework-compatible set of tools including, among others, a C# compiler and a Common Language Runtime.
IntroductionWhen we decided to run our ASP.NET application (ONLYOFFICE Community Server) on Linux, we had a choice between rewriting it from scratch or using Mono. We've chosen the Mono project.
The first attempt to make our application cross-platform was taken 4 years ago, but it failed. At that time Mono had a few limitations in porting an ASP.NET application from Windows to Unix. In particular, there was incomplete support for WCF (it lacked some critical features) and bugs in ASP.NET MVC. However, the Mono project has since then been actively developed by its community, thereby achieving the full support for .NET 4.0 and 4.5 features. That allowed us to restart our work in spring 2013. But even so, we faced many challenges for the first six months to make it work. We encountered problems whose description was not yet provided, and we solved them through trial and error approach
In this article, we'd like to share our experience, describe the issues we faced in the process - their reasons and solutions.
Issues, reasons and solutions while Migrating ASP.NET to Unix
Working with file systemProblem: site does not open, images are not displayed or displayed wrong. This issue has been highlighted and has a currently detailed description on Mono Project official website.
Reason: in MS Windows, slash and backslash are the same thing, in Unix these are two different paths. Also in MS Windows the paths are case independent, in Unix if the same name differs cases (lower case/upper case) that means they are different file names.
- change all the lines containing paths and directory dividers in them via the Path class. Use Path.Combine and Path.DirectorySeparatorChar everywhere in code.
- check all CSS files and correct their paths (including cases) for them to work properly in both operating systems.
- use the MONO_IOMAP=all environment variable for Mono to to ignore character case in paths.
Closing TransactionsProblem: transactions in MySQL database do not close/commit.
Reason: unfortunately could not be found.
Solution: added the AutoEnlist=false parameter to the connection string.
Difference in XML Mono Serializer performanceProblem: Difference in xml presentation for the fields with the null value and the exception in Mono serializer when empty namespaces are used in xml.
Solution: removed the Document module WCF service and substituted it with ASP.NET Web API.
Memory leak IssuesProblem: Memory leak when using mod_mono_server for apache2. E.g., when inserting new data in HttpRuntime.Cache with the key already present in the cache, Mono does not release memory taken by the previous data, as opposed to .NET.
Reason: still a mystery to us.
Solution: to solve the problem, we forced deletion of old data before the inserting the new one. We also switched to nginx where there is no such problem.
NullReferenceException type exceptionsProblem: WCF service threw the NullReferenceException when addressing the ConfigurationManager class members in case the HttpContext.Current property has been overwritten.
Reason: If you manually specify HttpContext.Current in non-web applications, Mono starts using WebConfigurationManager instead of ConfigurationManager which leads to exceptions in non-web application.
Solution: Got rid of manual specification of HttpContext.Current.
Incompatibility of Microsoft version and HTTP WCFReason: HTTP WCF Mono service has many small differences with Microsoft version e.g., small differences in xml serialization. In Mono version, extensions cannot be specified via attributes, only via configuration file. HttpContext.Current is absent in Mono version etc.
Solution: Document HTTP WCF service was rewritten with the ASP.NET Web API based version.
SignalR server crush during the load-testingReason: When exchanging messages in a chat embedded into the page, we used the ASP.NET SignalR library. When we ported to Mono, we wanted to use the same library there as well. It supports several types of data transfer transports: websockets, long polling, forever frame, server sent events. The most preferable for us is the websocket technology that we use with our SaaS version. Unfortunately currently SignalR does not allow to use websockets under Mono, that is why we decided to try and use Long Polling as transport. But at the stress test stage, the SignalR server kept crashing with System.IO.IOException: «Too many open files» after about 1k client connections. During investigating the problem further, socket leak was noticed at the SignalR server after client disconnection. We reported the issue to SignalR developers and hope that in next SignalR version the problem will be fixed.
Solution: Other transports are not suitable for our needs due to various reasons, that is why in current Mono version, we had to disable SignalR chat.
ConclusionWe made more than hundreds of changes to the ASP.NET project while porting it to Unix. As a result, we had one code base that works on Linux as well as on Windows. The source code is listed on GitHub.
If we started this work now, we would use ASP.NET 5 instead. Its release was announced when our work was nearly over!
About Tatiana Kochedykova -
Tatiana is a technical writer and translator working now at Ascensio System SIA, developer of the productivity solutions. She tries to spend most of her time with her little son and husband. Optimistic by nature, she knows for sure that all will be well.