Uno Platform development gotcha's

In the past month, I played around with uno platform. If you don’t know about the uno platform, it is a way to create cross-platform applications with .NET. They make two bold claims that got my attention to try it out.

  • Pixel-Perfect Multi-Platform Applications with C# and WinUI
  • The first and only UI Platform for single-codebase applications for Windows, WebAssembly, iOS, macOS, Android and Linux

My plan was to transform one of my projects, egyketto.ro, into a mobile app run on Android and iOS.

egyketto.ro uno transform

Having experience with WPF, it was relatively easy to get into writing XAML code for the UI. After a few days, I can say that the applications booted up on both platforms. Still, I have learned some things during this experience.

Gotcha’s

It actually works :)

A UWP app can be transformed quickly into a Mobile App. In my experience, the project was created from scratch, and an MVP could be delivered in a week. There is still room for optimization, but it works way better than I expected on the first run.

Builds can take a lot of time

Because you are actually creating Windows, WebAssembly, iOS, macOS, Android and Linux apps simultaneously, building may take up more time. The same error can show up several times and working with emulators, well, is still like working with emulators and not real devices.

The good thing is that there are ways to get around this.

  • Just delete the project you do not care to develop for.
  • Create a development build config that would only build for one platform. Removing everything and working just for UWP until you have something to test on other devices could significantly speed up the development cycle.

UWP only config

References may seem strange

The base references are not always uno specific references. For example, file manipulation for all platforms is achieved by referencing Windows.Storage package.

For HttpHander the case is different. If you want to support WASM you will need a specific reference for it. This is due to the particular nature of WASM, but be aware of this quirk.

A possible solution is using directives like:

#if __WASM__
    var innerHandler = new Uno.UI.Wasm.WasmHttpHandler();
#else
    var innerHandler = new HttpClientHandler();
#endif
    _client = new HttpClient(innerHandler);

Information is there, but not in the traditional form

When receiving an error or searching for a solution, one should use other frameworks keywords as search parameters. With XAML issues, I found that using UWP or WPF keywords would bring me better results. For Mobile platform issues, using Xamarin as a keyword worked better. There are already uno platform-specific questions on Stackoverflow, but there are still just a few compared to the older technologies.

Besides this, there are new and new technical blog posts showing up, and they have a really active discord channel that you should check out if you run into a problem.

Update Asp.NET Core 3.1 create-react-app project to .NET 5

Updating a Asp.NET Core application to a new version of .NET is a relatively straightforward experience. From .NET 5, the two frameworks, .NET and .NET-core are merging. In the future, .NET will be able to build for any target system. With this version of .NET the app gets faster and also uses less memory.
To Upgrade the ASP.NET Core Web 3.1 app to ASP.NET Core 5, follow these steps.

Prequisites

.NET 5 installed, can be downloaded here.
Visual Studio 2019 updated to the latest version.

Update Target framework to .NET 5

Open the project in Visual Studio.
Go to Properties -> Application -> Target framework -> from the dropdown list select NET 5.0

Upgrade ASP.NET Core Web 3.1 app to ASP.Net 5.0

or open the project .csproj file and change the TargetFramework to net5.0

...
  <PropertyGroup>
-    <TargetFramework>netcoreapp3.1</TargetFramework>
+    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>
...

Update NuGet packages

Manage NuGet Packages

In the NuGet package manager, several packages should be updated to the new versions.
For me this included the updates for packages:

  • Microsoft.AspNetCore.SpaServices.Extensions
  • Microsoft.Extensions.Logging.AzureAppServices
  • Microsoft.VisualStudio.Web.CodeGeneration.Design

Update npm packages (optionally)

This is not a requirement as it is separate from the .NET Framework, none the less it is essential to keep the libraries up to date, so the vulnerabilities are mitigated.

In the client app folder, open up a PowerShell or command line and run:

npm update
npm audit fix

Update docker images

My project is in a docker container, so the images need to be also updated.

Note: There is a breaking change (more here) in the new images that they have dropped git/curl/wget from the images so this is a change that needs to be addressed in the docker file.

In the dockerfile, I separate the build from the final version. This results in a smaller image size at the end. The project is built with the full SDK docker image. The published version is on an ASP.NET slim image. This usually saved a couple of hundred MBs depending on the project size.

FROM mcr.microsoft.com/dotnet/aspnet:5.0-buster-slim AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
RUN apt-get update && apt-get install -y --no-install-recommends \
		curl \
	&& rm -rf /var/lib/apt/lists/*
RUN curl -sL https://deb.nodesource.com/setup_15.x | bash -
RUN apt-get install -y nodejs

FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /src
RUN curl -sL https://deb.nodesource.com/setup_15.x | bash -
RUN apt-get install -y nodejs
COPY ["DotNetProject/DotNetProject.csproj", "DotNetProject/"]
RUN dotnet restore "DotNetProject/DotNetProject.csproj"
COPY . .
WORKDIR "/src/DotNetProject"
RUN dotnet build "DotNetProject.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "DotNetProject.csproj" -c Release -o /app/publish -r linux-x64 -p:PublishTrimmed=True

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "DotNetProject.dll"]

For an even smaller image, one can use the alpine linux docker image. It produces a considerable smaller docker image. I have added this configuration file also below. There is a difference on how you add the necessary npm/nodejs package.

FROM mcr.microsoft.com/dotnet/aspnet:5.0-alpine AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
RUN apk add --update npm

FROM mcr.microsoft.com/dotnet/sdk:5.0-alpine AS build
WORKDIR /src
RUN apk add --update npm
COPY ["DotNetProject/DotNetProject.csproj", "DotNetProject/"]
RUN dotnet restore "DotNetProject/DotNetProject.csproj"
COPY . .
WORKDIR "/src/DotNetProject"
RUN dotnet build "DotNetProject.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "DotNetProject.csproj" -c Release -o /app/publish -r linux-musl-x64 -p:PublishTrimmed=True

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "DotNetProject.dll"]

The result

For the docker images sizes, the buster image version did not change much. One could say that it remained the same. The alpine image, however, with everything needed for the asp.net create-react-app is 100+ MB smaller. It is worth taking into consideration if the size is an issue.

Docker Image sizes

Performance-wise the two new images were faster than the .NET 3.1 image. The startup has greatly improved. After publishing it to Azure, comparing the average memory working set also showed a significant drop in memory usage. From 300MB, it dropped now to 200MB.

Average memory working set

Kudos to the .NET guys!

Add Https to Azure Web App with Let's Encrypt

You have deployed a web app docker container to Azure Web App service. Now you want to enable Https for the web application. This article will show you, how you can manage that from a Windows operating system. The creation and addition in this case will be manual. The certificate will be created with the help of Let’s Encrypt. To transform the certificate into the right format for Azure, OpenSSL will be used.

Note: If you do not have a custom domain name, you should consider the Certificate service from Azure. This is a free service, it creates a Digicert certificate and it also renews it automatically. It comes with limitations though.

To create a Let’s Encrypt certificate, first, you should download the Certbot program from their site. The windows version is still in beta, but it works nonetheless. You can get it from here or check out their site for instructions on how to install it here.

After installing the Cerbot you should be able to call certbot commands from cmd or powershell.

Issuing a certificate

The following command needs to be executed to create a certificate:

certbot certonly -d testDomain.com -d www.testDomain.com --manual --preferred-challenges dns
  • certonly - only creates a certificate, does not install it on the machine
  • -d - domain name specifier, you can add multiple domains, subdomains for a certificate
  • –preferred-challenges dns - defines how you prove that the domain is under your administration. In this case with a DNS challenge. A DNS challenge requires you to add a DNS TXT record on your domain.

If you use the DNS Zones service from Azure, you should add a new Record like this.

Azure DNS Record

After the DNS challenge is successfully made, you should have a message, that is successfully created the certificate. For the default installation the path where the certificate was exported is C:\Certbot\archive.

The following files should be there:

  • cert.pem
  • chain.pem
  • fullchain.pem
  • privkey.pem

Converting the certificate for Azure

Azure requires a private certificate in the PKCS#12 file format. Certbot does not generate it out of the box, but we can convert it to the right format with OpenSSL.

For a pfx certificate this command needs to be run: with the files from the previous step.

openssl pkcs12 -export -out certificate.pfx -inkey privkey.pem -in cert.pem -certfile chain.pem

Note: You will have to give a password in this process, this password will be used later when we upload the certificate.

Uploading certificate to Azure Web App service

Open App Service from the Azure Web portal. From the left navigation of your app, select TLS/SSL settings > Private Key Certificates (.pfx) > Upload Certificate. Then add binding to the Custom Domain under the Custom Domain section.

More on this, in the official documentation: https://docs.microsoft.com/en-us/azure/app-service/configure-ssl-certificate#upload-certificate-to-app-service