<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://mallibone.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://mallibone.com/" rel="alternate" type="text/html" /><updated>2026-05-08T14:24:06+00:00</updated><id>https://mallibone.com/feed.xml</id><title type="html">Mark&apos;s Blog</title><subtitle>Contents on this Site are considered to be mobile friendly.</subtitle><author><name>Mark Allibone</name></author><entry><title type="html">Developing your .NET app using virtual serial ports on macOS</title><link href="https://mallibone.com/post/dotnet-on-macos" rel="alternate" type="text/html" title="Developing your .NET app using virtual serial ports on macOS" /><published>2025-04-04T00:03:00+00:00</published><updated>2025-04-04T00:03:00+00:00</updated><id>https://mallibone.com/post/dotnet-on-macos</id><author><name>Mark Allibone</name></author><category term=".NET" /><category term="macOS" /><category term="socat" /><category term="dev life" /><category term="IoT" /><summary type="html"><![CDATA[It’s the year 2025, and I’m writing this blog post about serial ports—also known as RS232. While this might feel like a blast from the past for many, if you’re working in IoT or IIoT, you might find yourself writing a .NET app that communicates over a serial port. Using virtual serial ports allows you to test your app without needing any hardware connected to your development machine. So let’s go over the steps you need to take and a simple setup to verify that everything works as expected. Since we’re on a Mac (I’m using a MacBook Pro with an M2 Max), the first step is to install socat via the command line. Open your terminal and enter the following command: brew install socat This installs socat—a very handy tool for creating virtual serial (RS232) ports. In fact, socat does much more: it can relay all kinds of communication protocols such as TCP, UDP, and Unix sockets. So if you’re looking for a powerful routing tool on a Unix-like system, this might be your tool of choice. But back to our serial ports. With the following command, we’ll set up two virtual serial ports, where the TX of the first port is “wired” to the RX of the second one - and vice versa: socat -d -d pty,raw,echo=0 pty,raw,echo=0 After running this command, you should see something along the lines of: 2025/04/03 14:08:15 socat[68927] N PTY is /dev/ttys000 2025/04/03 14:08:15 socat[68927] N PTY is /dev/ttys001 2025/04/03 14:08:15 socat[68927] N starting data transfer loop with FDs [5,5] and [7,7] On my machine, the serial sockets were created under /dev/ttys000 and /dev/ttys001—but this may differ on your system, so make sure to check your terminal output rather than relying on the example above. With the sockets and wiring in place, let’s write a basic .NET app to verify that everything works. .NET doesn’t include built-in support for RS232, so you’ll need to install the System.IO.Ports NuGet package. dotnet add package System.IO.Ports Now, let’s take a look at a simple echo console app: using System.IO.Ports; var portName = "/dev/ttys001"; // change as needed using var serialPort = new SerialPort(portName, 9600); serialPort.Open(); Console.WriteLine($"Echo server on {portName}"); serialPort.DataReceived += (s, e) =&gt; { var sp = (SerialPort)s; string incoming = sp.ReadExisting(); Console.WriteLine("Received: " + incoming); sp.Write(incoming); // Echo back Console.WriteLine("Echo"); }; Console.ReadLine(); Note the portName and the baud rate (set to 9600)—these might be different for your setup. Other than that, this service will simply echo back whatever you send to it. If you like, you could extend it to simulate responses for specific commands, giving you a basic simulation environment for a device. But I’ll leave that up to you, in case you’re ready to go down that rabbit hole. 😉 We can now start the echo service, and it should simply connect and wait to receive data. For demonstration purposes, let’s write a small app that lets us enter messages to be sent to ttys000 - and we should receive them back immediately. using System.IO.Ports; using System.Text; Console.WriteLine("RS232 Terminal Application"); // Setup variables var serialPort = new SerialPort("/dev/ttys000", 9600); var running = true; try { // Open port and handle exit serialPort.Open(); // .. // Start read task _ = Task.Run(() =&gt; { var buffer = new byte[1024]; while (running) { // ... var bytesRead = serialPort.Read(buffer, 0, Math.Min(buffer.Length, serialPort.BytesToRead)); Console.Write($"Received: {Encoding.ASCII.GetString(buffer, 0, bytesRead)}"); // ... } }); // Main send loop Console.WriteLine("Type messages (Ctrl+C to exit):"); while (running) { var message = Console.ReadLine(); if (!string.IsNullOrEmpty(message) &amp;&amp; running) { try { serialPort.WriteLine(message); } catch (Exception ex) { Console.WriteLine($"Send error: {ex.Message}"); } } } } // ... With this app, you can now send messages and see them echoed back—just like that, you’ve got an easy way to develop and test COM port functionality without needing to attach any physical devices. To clean up the virtual ports created by socat, simply stop the process using Ctrl+C. The virtual ports will then be removed automatically. You can find the complete sample application from this blog post on GitHub. HTH]]></summary></entry><entry><title type="html">Getting fit with MAUI Reactor</title><link href="https://mallibone.com/post/maui-reactor-fit" rel="alternate" type="text/html" title="Getting fit with MAUI Reactor" /><published>2024-07-09T00:03:00+00:00</published><updated>2024-07-09T00:03:00+00:00</updated><id>https://mallibone.com/post/maui-reactor-fit</id><author><name>Mark Allibone</name></author><category term=".NET" /><category term=".NET MAUI" /><category term="MAUI Reactor" /><summary type="html"><![CDATA[MAUI Reactor is a code first, MVU style framework based on .NET MAUI that promises less ceremony when writing your apps. And I like the sound of that and since this post will be part of MAUI UI July I wanted to tackle a long hedged goal of mine which is to write a app that will keep me motivated during my home workouts. This app has been heavily inspired by the WODTimer app. If you are looking for a sound and complete app (for free) be sure to check it out. I have been working out at home for quite some time now. And while I have started to join again in some group training, I often find myself in my home/hotel/outdoor gym by myself. Finding motivation to workout can be tough, but time boxing the training is a great helper on my end. There are multiple forms on workout timers out there and I want to add a few of them into my app. These are for example: EMOM AMRAP For Time TABATA I will dive in later what these modes mean but for now, I want to have a general overview which will allow me to select the workout mode. Once on the page I will want to configure the workout timer. Depending on the option chosen I will need to set different options. Then I want to start the workout. In this first post we will focus on the start page and the AMRAP workout since it is the simplest and will let me show how you can work with setting up the pages, navigation (incl. parameters) and share styling accross pages. Setting things up MAUI Reactor builds on top of .NET MAUI, so first we will want to have .NET MAUI installed and properly configured. Next you can install MAUI Reactor, don’t forget to also install the tooling that enables hot reload, since it will greatly improve your productivity, when developing an app. As for the editor - you can choose whatever you want. I have been using it with Visual Studio on Windows; Rider and VS Code on macOS. I decided to use the Shell for navigation - especially since MAUI Reactor comes with a nice template. dotnet new maui-reactor-appshell -n AlohaFit The start page With everything setup, let’s have a look at the main page. The main thing I want to do is have a list of buttons - this works well for the limited amount of workout types in the app. The general setup of the page looks as follows: using AlohaFit.Types; using MauiReactor; namespace AlohaFit.Pages; class MainPage : Component { private async void NavigateToAmrap() =&gt; await Microsoft.Maui.Controls.Shell.Current.GoToAsync&lt;WorkoutParameters&gt;(NavigationRoutes.WorkoutPage, param =&gt; param.SelectedWorkoutMode = WorkoutModes.AMRAP); // Other workout navigation methods public override VisualNode Render() =&gt; ContentPage( ScrollView( VStack( Label("TIMER").Style("Headline").HCenter().Margin(0,0,0,32), Components.PrimaryButton("AMRAP", Colors.DarkOrange, NavigateToAmrap), // Other workout timer buttons ) .VCenter() .Spacing(25) .Padding(30, 0) ) ); } Since the buttons all look the same we can easily extract a component that will give us the functionality and consistent style. using System; using MauiReactor; namespace AlohaFit.Pages; public static class Components { public static Button PrimaryButton(string title, Color color, Action handleOnClicked) =&gt; new Button(title) .BackgroundColor(color) .CornerRadius(24) .HeightRequest(50) .WidthRequest(200) .OnClicked(handleOnClicked) .HCenter(); } Extracting components and reusing them is nothing new. It can be done in .NET MAUI, but using C# makes this approach a lot more terse. But let’s have a look at our options when it comes to creating reusable views and styling in general. Working with styles Especially since the properties we have are simply function parameters for smaller things. If we wanted to create an entire View - we can do that too, and we can then even provide properties that we can reuse - you can read more about that in the MAUI Reactor docs. When working on apps it is common to extract styles. When working with MAUI Reactor we can do exactly that too. If we look under Resources/Styles, we will find xaml files for the colour and the styles for our components. We can change the styles and the components used in MAUI Reactor will actually use those styles. Which is super neat, because this means you can reuse any existing mobile styles you might already use. But let’s get back to our workout timer. The workout timer Generally speaking all workout timers require configuring the timer. Once configured the user can start the timer. When running the timer will complete or the user can abort. For now I will focus on the AMRAP timer, but in the near future I will want to add the other timers. Since they all seem very similar in function I will try to create a workout page which will render itself based on the mode and the features each timer will provide. So when choosing a timer I will want to open the workout page and pass in as a parameter which workout has been chosen. The navigation Since we are using the Shell we can configure the navigation as follows: class AppShell : Component { protected override void OnMounted() { MauiControls.Application.Current.UserAppTheme = AppTheme.Dark; Routing.RegisterRoute&lt;WorkoutPage&gt;(nameof(WorkoutPage)); base.OnMounted(); } public override VisualNode Render() =&gt; Shell(ShellContent().Title("").RenderContent(() =&gt; new MainPage())); } And invoke the navigation by using the Shell in the MainPage. await Microsoft.Maui.Controls.Shell.Current.GoToAsync&lt;WorkoutParameters&gt;(NavigationRoutes.WorkoutPage, param =&gt; param.SelectedWorkoutMode = WorkoutModes.AMRAP); Note we are also passing a navigation parameter. MAUI Reactor uses a class as navigation parameter. I kind of like this, because it extends the Shell navigation but also provides type safety. The workout page defines the parameter as additional generic argument WorkoutParameters - which is the name of the class: class WorkoutPage : Component&lt;WorkoutState, WorkoutParameters&gt; { // ... } And we can access the parameters via the Props attribute in a Component in the code: Props.SelectedWorkoutMode I really like this approach. It gives me the typesafety of a language like C#, but also requires very little fuss when doing so. Be sure to check the MAUI Reactor docs to get the full picture of the Shell navigation in MAUI Reactor. Dealing with state With the navigation and the parameters out of the way. Let’s implement the timer. For this we will need some state. The standard MAUI app usually relies on the MVVM pattern for dealing with seperation of View ViewModel and Models. But in MAUI Reactor we use a Model View Update (MVU) based approach. The state of the page is stored in a seperate class (the Model of MVU). But before we dive into that I feel like owing you an explanation of what AMRAP stands for… AMRAP (As Many Rounds/Reps As Possible): If you have a given workout for example 5 Pullups, 10 Pushups and 20 Air Squats and your AMRAP is 15 minutes long. You will try to do as many rounds as possible. You should generally try to be able to keep on working out without any larger breaks. So your pace will become a factor once you have built the muscular endurance. With that out of the way - let’s look at what information we need when setting our timer: public class AmrapState { public DateTimeOffset StartTime { get; set; } public TimeSpan Duration { get; set; } public TimeSpan RoundDuration { get; set; } public int Rounds { get; set; } public bool IsRunning { get; set; } public IReadOnlyCollection&lt;DurationOption&gt; DurationOptions { get; set; } public int SelectedDurationIndex { get; set; } } The wiring of the view to the state is done with like with the navigation parameters via a generic attribute. Starting the workout With all this in place - we are left with starting the workout. So once the user hits the button the timer starts: private void StartWorkout() { SetState(s =&gt; s.IsRunning = true); SetState(s =&gt; s.StartTime = DateTimeOffset.Now); // ... _timer.Start(); } The wiring up is very straight forward. You can also see that choosing between the configuration or running sub-view is done by observing a boolean in the state. Further note that we do not have to implement any events, raise property changed or the like. It’s simply calling SetState and assigning the property. So less hassle, same effect.. I would call that neat. 😃 And that is my basic AMRAP timer using MAUI Reactor. Looking forward I hope this post could give you an overview of what Reactor MAUI is and how it extends .NET MAUI to provide a different approach in writing native apps. In future posts I will expand this app to provide different workout modes, expand the design. So stay tuned for future updates. Also be sure to check out Reactor MAUIs GitHub page and the docs. You can find the app to this post on here. HTH]]></summary></entry><entry><title type="html">Autostarting your (ASP.NET Core) websites on IIS</title><link href="https://mallibone.com/post/iis-autostart-website" rel="alternate" type="text/html" title="Autostarting your (ASP.NET Core) websites on IIS" /><published>2024-02-26T00:03:00+00:00</published><updated>2024-02-26T00:03:00+00:00</updated><id>https://mallibone.com/post/iis-autostart-website</id><author><name>Mark Allibone</name></author><category term="IIS" /><category term="ASP.NET Core" /><summary type="html"><![CDATA[You might have an ASP.NET Core app that you deploy to IIS (Internet Information Server) that, for example, listens in the background to incoming IoT Signals from a Message Queue, but it only does so after you have visited the server in the browser. Having an app that should do background work but does not start automatically can be cumbersome at best and lead to outages, aka angry support calls, at worst. Let’s look at how we can ensure an app is started automatically on startup. How can we change this to make your app appear automatically after deployment? You might ask yourself: “Okay, let’s see how we can configure a site to come up automatically. But why do I even have to do this in the first place?” - The answer is quite simple. IIS does this to save resources. Reducing resource usage allows hosting a plethora of sites on your server, and IIS will ensure that sites that are not in use will spin down, aka only get started once they are in need. While this is great if you have many small sites to run that only run once in a while, it’s a pain in the neck if you have a site that should always be up and running since it does more things than respond to HTTP requests. IIS Setup Usually, hosting your site on IIS will run on a Windows Server once you hit production. IIS is installed using the Server Manager and added under Roles and Features. The Application Initialization role service is not added by default when enabling IIS. Be sure it is enabled for auto-start to work. IIS app configuration Microsoft docs already cover deploying your app to IIS on a Windows server. After that, change the Start Mode to Always Running under Advanced Settings of the AppPool. Then go to the website and set Always Running to true in the Advanced Settings. Your app will automatically start running once the app pool starts up. Note that you will only want this for some apps you are developing. But whenever you have an app that also serves Requests over a different interface than HTTP - for instance, RabbitMQ, MQTT, SQL, file-based, etc., you will either have to remember always to visit the page or else it will not respond as you might intend. HTH]]></summary></entry><entry><title type="html">Accessing a private Azure DevOps NuGet feed when using Docker build containers</title><link href="https://mallibone.com/post/docker-build-private-nuget-feed" rel="alternate" type="text/html" title="Accessing a private Azure DevOps NuGet feed when using Docker build containers" /><published>2023-12-31T00:03:00+00:00</published><updated>2023-12-31T00:03:00+00:00</updated><id>https://mallibone.com/post/docker-build-private-nuget-feed</id><author><name>Mark Allibone</name></author><category term="Docker" /><category term="DevOps" /><category term="NuGet" /><category term="Azure DevOps" /><summary type="html"><![CDATA[Using Docker for your .NET builds gives you a reproducible way to execute your builds on your build server and developers’ devices. Setting up Docker to build your .NET applications is a straightforward process, but what if you use a private Azure DevOps NuGet feed that has to be accessed to build your app? Well, let’s find out. 🤓 In this example, Docker is used to build an ASP.NET Core application. Let’s start by looking at a Dockerfile defining the image we want to create for our build. Microsoft offers a few of the images we can use for this task. At the time of writing, .NET 8 was the latest image, so we will be using this for our app: FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0 AS build COPY src/. /source WORKDIR /source/Hello.AspNet RUN --mount=type=cache,id=nuget,target=/root/.nuget/packages \ dotnet publish --use-current-runtime --self-contained false -o /app # Rest of the file using the build output FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final WORKDIR /app # Copy everything needed to run the app from the "build" stage. COPY --from=build /app . # ... run app One advantage of using Docker to build your app is that it will create a reproducible build environment. We can ensure that even in a few years when .NET 8 is no longer the current standard, we can use the same image used during the app’s creation. It is not uncommon to extract standard functionality into a library. Many companies use private feeds to post libs for reuse in other closed-source applications. Azure DevOps provides a way to host said libraries. A private feed for NuGet, NPM, et al. can be created and used by publishing to Artefacts. But how can we enable our Docker to build a container to access that feed? First, we will have to inform NuGet what the package sources are. For this, we can define a nuget.config file: &lt;?xml version="1.0" encoding="utf-8"?&gt; &lt;configuration&gt; &lt;packageSources&gt; &lt;!--To inherit the global NuGet package sources remove the &lt;clear/&gt; line below --&gt; &lt;!-- &lt;clear /&gt; --&gt; &lt;add key="Private Feed" value="https://pkgs.dev.azure.com/my-org/_packaging/my-private-feed/nuget/v3/index.json" /&gt; &lt;add key="nuget.org" value="https://api.nuget.org/v3/index.json" /&gt; &lt;/packageSources&gt; &lt;/configuration&gt; You can place the nuget.conifg at the root of the project. Alternatively, copy the file before the build starts: FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0 AS build COPY src/. /source COPY nuget.config /source/Hello.AspNet # ... unchanged from the docker file above The dotnet build will know where to look but will need authorization access. We can use a Personal Access Token, or PAT for short. The PAT will require read access enabled under the Packaging section. Creating a PAT is a simple process, and I will point you to the official docs to ensure that a future redesign will not be the source of any confusion. 😉 If you create a PAT, you can reuse it. However, since the generated code is only shown right after generating it, storing it in your password manager of choice is a good idea if you want to reuse the PAT for other build tasks. With the PATat hand, we now have different options for using it. The most straightforward way is to put it into the NuGet.config file: &lt;?xml version="1.0" encoding="utf-8"?&gt; &lt;configuration&gt; &lt;packageSources&gt; &lt;!-- The package sources --&gt; &lt;/packageSources&gt; &lt;packageSourceCredentials&gt; &lt;keyName&gt; &lt;add key="Username" value="Will-be-ignored" /&gt; &lt;add key="ClearTextPassword" value="THE-PAT-GENERATED-BY-AZURE-DEVOPS" /&gt; &lt;/keyName&gt; &lt;/packageSourceCredentials&gt; &lt;/configuration&gt; While adding the PAT credentials directly to the file will fix the authorization issue. It will also mean that your PAT to source control. It could be better, but what are the alternatives? One way would be to set the PAT as an environment variable. So we leave the nuget.config as follows: &lt;?xml version="1.0" encoding="utf-8"?&gt; &lt;configuration&gt; &lt;packageSources&gt; &lt;!--To inherit the global NuGet package sources remove the &lt;clear/&gt; line below --&gt; &lt;!-- &lt;clear /&gt; --&gt; &lt;add key="Private Feed" value="https://pkgs.dev.azure.com/my-org/_packaging/my-private-feed/nuget/v3/index.json" /&gt; &lt;add key="nuget.org" value="https://api.nuget.org/v3/index.json" /&gt; &lt;/packageSources&gt; &lt;packageSourceCredentials&gt; &lt;keyName&gt; &lt;add key="Username" value="Will-be-ignored" /&gt; &lt;add key="ClearTextPassword" value="%THE_PAT%" /&gt; &lt;/keyName&gt; &lt;/packageSourceCredentials&gt; &lt;/configuration&gt; And set the environment variable using Docker as follows: FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0 AS build COPY src/. /source WORKDIR /source/Hello.AspNet ARG FEED_PAT # ... build and run app If you are triggering the Docker container build via the Azure DevOps build pipeline, you can set variables or give your build run access via the NuGetAuthenticate build step. This will allow your build to access the private NuGet feed without the PAT showing up in any of your files, i.e. git repo. HTH]]></summary></entry><entry><title type="html">Setting up Tailscale on your QNAP NAS</title><link href="https://mallibone.com/post/tailscale-qnap" rel="alternate" type="text/html" title="Setting up Tailscale on your QNAP NAS" /><published>2023-10-31T12:03:00+00:00</published><updated>2023-10-31T12:03:00+00:00</updated><id>https://mallibone.com/post/tailscale-qnap</id><author><name>Mark Allibone</name></author><category term="VPN" /><category term="Home Automation" /><summary type="html"><![CDATA[By discovering Tailscale, I finally found a solution for my QNAP NAS as a VPN gateway into my home network. I have a couple of devices at home that do not talk to some cloud service but provide services when accessed through my home network. Whenever I am not at home, accessing said services is no longer possible. Let’s examine how Tailscale enables accessing devices on the same network as your QNAP NAS. Tailscale is a VPN (Virtual Private Network) service that enables devices to communicate securely with each other. You must set up an account by following the steps described here. You can install a client on your phone, tablet and/or laptop. Next, be sure to install Tailscale on your QNAP NAS. You can choose your NAS as your exit node. This is nice if you are abroad and want to access websites/movie services only available in your home country. Open the app on your QNAP and select advertise as exit node to enable this feature. But there is more. What if you want to access devices only available on your network at home. For this, you have to enable the local bridge mode. This will require you to access your QNAP system via SSH. You will have to ensure that SSH is enabled on your QNAP NAS. Next, an SSH client is required. If you are on macOS, use Terminal. On Windows, you can use the Windows Terminal to connect to your NAS: ssh your-nas-user@your-nas-ip-or-dns After successfully logging in, depending on your QNAP NAS, you must locate where your Tailscale app has been installed. You should find it in either one of these two locations: /share/CACHEDEV1_DATA/.qpkg/ /share/MD0_DATA/.qpkg/ You can check if the NAS exists using the ls THE-PATH command. If a list of directories is returned, you have found the right one. Next, invoke the following command - in my case, it was the second option, adjust to your NAS accordingly: /share/MD0_DATA/.qpkg/tailscale/tailscale -socket /tmp/tailscale/tailscaled.sock up --advertise-routes=\192.168.1.0/24 --advertise-exit-node --reset Note that you might have to change the IP according to the IP of your local network. The /24 is for the Subnet and says how many Bits are used to identify the network, e.g. shorthand for 255.255.255.0. Once this command runs through, open up the Tailscale app. You will be able to enable your NAS as a logical bridge. Once enabled and connected to your Tailscale VPN, you can see all your local network devices. HTH]]></summary></entry><entry><title type="html">Writing UI tests for .NET MAUI - POC</title><link href="https://mallibone.com/post/uitest-maui-101" rel="alternate" type="text/html" title="Writing UI tests for .NET MAUI - POC" /><published>2023-07-26T00:03:00+00:00</published><updated>2023-07-26T00:03:00+00:00</updated><id>https://mallibone.com/post/uitest-maui-101</id><author><name>Mark Allibone</name></author><category term=".NET MAUI" /><category term="UI Test" /><summary type="html"><![CDATA[This blog post is part of the .NET MAUI UI July - be sure to check out the other blog posts. 🙂 Automated UI tests allow you to write tests that execute your app as a user would. This can be a significant selling point compared to a unit, integration, and other non-UI automated tests. Non-technical users are no longer left with a chart of how many passed, failed, skipped, etc. - they actually get to see what the tests performed. At first glance, automated UI tests seem like the ultimate automated tests, but more later. If you have written Xamarin apps in the past, you might have heard about Xamarin UI Tests. The testing framework based on Calabash and NUnit allows you to write automated UI tests. Sadly for users of said framework, there never has been a .NET MAUI UI Test successor - or has there? This post will examine how to use the Xamarin UI Test framework to write .NET MAUI UI tests. Foreword Before we start, I want to be honest with you. A lot of the following steps may seem odd. While you will see how to write UI tests using the Xamarin UI Test framework, a few parts remain left to be migrated to certain foundations that .NET MAUI is built, such as using .NET. So if a few of the following details feel “hacky” - perhaps one could even say “yucky”. I would concur but also add that only if there is actual usage of a product will any further development be put behind it. So, in the hopes of UI tests living onwards, let’s look at how we can cobble together - erh, write UI tests for .NET MAUI. Writing Tests UI tests are currently supported for Android, iOS and MacCatalyst. Given the “Create new .NET MAUI Project” starting point, we first want to add the test project using your favourite .NET IDE. Therefore, we add a .NET Framework 4.8 library project. That is not a typo; we add a .Net Framework 4.8 library project. Next, open the newly added csproj and change it to the following lines: &lt;Project Sdk="Microsoft.NET.Sdk"&gt; &lt;PropertyGroup&gt; &lt;TargetFramework&gt;net481&lt;/TargetFramework&gt; &lt;IsPackable&gt;false&lt;/IsPackable&gt; &lt;IsTestProject&gt;true&lt;/IsTestProject&gt; &lt;/PropertyGroup&gt; &lt;ItemGroup&gt; &lt;PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" /&gt; &lt;PackageReference Include="NUnit" Version="3.13.3" /&gt; &lt;PackageReference Include="NUnit3TestAdapter" Version="4.5.0" /&gt; &lt;PackageReference Include="NUnit.Analyzers" Version="3.6.1" /&gt; &lt;PackageReference Include="coverlet.collector" Version="3.2.0" /&gt; &lt;PackageReference Include="SharpZipLib" Version="1.4.2" /&gt; &lt;PackageReference Include="Xamarin.UITest" Version="4.2.0" /&gt; &lt;/ItemGroup&gt; &lt;/Project&gt; With all this in place, we are ready to write our first UI test for Android. We will get back to the Apple platforms in a minute. UI Tests require more setup. We generally have to pass in the platform, which allows the test framework to translate your test code into actual UI actions. Since you will do this setup for every test class, it is often moved to a base class: using System; using NUnit.Framework; using Xamarin.UITest; namespace MauiTesting101.UiTests { [TestFixture(Platform.Android)] [TestFixture(Platform.iOS)] public class BaseTest { IApp _app; protected readonly Platform Platform; protected BaseTest(Platform platform) =&gt; Platform = platform; protected IApp App =&gt; _app ?? throw new NullReferenceException(); [SetUp] public virtual void BeforeEachTest() { _app = AppInitializer.StartApp(Platform); App.Screenshot("App Initialized"); } } } We can write our first test with the UI test setup out of the way. Since we are doing this for the .NET MAUI “Hello World” app, let’s check if the button changes its text accordingly after it gets selected: [Test] public void WhenCounterClicked_IncrementCount() { // Arrange: Wait until counter Button is present const string countButton = "CounterBtn"; App.WaitForElement(countButton); // Act: Tap the button App.Tap(countButton); // Assert: check label updated var buttonLabelValue = "Clicked 1 time"; Assert.DoesNotThrow(() =&gt; App.WaitForElement(x =&gt; x.Marked(buttonLabelValue)), "Button was not clicked"); // Take a screenshot App.Screenshot("Tapped 1 time"); } Now, let’s look at some of the things going on. The arrange, act, assert style of writing the test is optional, but I like the structure it gives the tests. One thing that you will quickly notice when writing UI tests is that everything takes more time. The test code must wait for the app to start before we tap the button. And this is what is happening on this line: [Test] public void WhenCounterClicked_IncrementCount() { // Arrange: Wait until counter Button is present ... App.WaitForElement(countButton); // Act ... } This line will either timeout (UI element not found) or complete without exception. The second one means the UI element is present. Once that is the case, we invoke the actual tapping of the button: [Test] public void WhenCounterClicked_IncrementCount() { // Arrange: Wait until counter Button is present ... // Act: Tap the button App.Tap(countButton); // Assert: check label updated ... } Then we wait again for the UI to update and verify the value has changed according to our expectations. Even with this relatively simple test, you notice a crucial point when writing UI tests. Before you invoke an action, you ensure that the expected UI is loaded by waiting for a UI element. You may have noticed that we invoke the button using an identifier string CounterBtn. This tag is set on in the XAML by setting the AutomationId property: &lt;?xml version="1.0" encoding="utf-8" ?&gt; &lt;ContentPage ...&gt; &lt;ScrollView&gt; &lt;VerticalStackLayout ...&gt; ... &lt;Button x:Name="CounterBtn" AutomationId="CounterBtn" ... /&gt; &lt;/VerticalStackLayout&gt; &lt;/ScrollView&gt; &lt;/ContentPage&gt; The AutomationId property is not mandatory but significantly reduces the time to write a UI test. It also significantly improves the quality of your tests since you will not have to rewrite code if, for example, the UI hierarchy changes. But it does require that during development (or when writing the UI test), the AutomationId Property is set. If we execute the test, we will see the following: Next up is iOS. For iOS, we must install the Xamarin.TestCloud.Agent NuGet package. This is required to invoke the UI commands. Since .NET MAUI uses a single project file, we must ensure that this NuGet package is only added when compiling against iOS. The same applies to macCatalyst. When adding the NuGet to the solution, this is automagically done for us: &lt;Project&gt; ... &lt;ItemGroup Condition="'$(TargetFramework)' == 'net7.0-ios'"&gt; &lt;PackageReference Include="Xamarin.TestCloud.Agent" Version="0.23.2" /&gt; &lt;/ItemGroup&gt; &lt;ItemGroup Condition="'$(TargetFramework)' == 'net7.0-maccatalyst'"&gt; &lt;PackageReference Include="Xamarin.TestCloud.Agent" Version="0.23.2" /&gt; &lt;/ItemGroup&gt; &lt;/Project&gt; I had some issues with this approach, so I changed the lines to the following: &lt;ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'"&gt; &lt;PackageReference ... /&gt; &lt;/ItemGroup&gt; Generally speaking, this ensures the NuGet package is only added when compiling against iOS or macCatalayst. We could further check that the package only gets used for Debug builds. This would ensure we don’t submit our app with this NuGet package to the store. It has been heard that the package can lead to rejection during the store evaluation process. But then again, sometimes you want to test against the release build, which could require dedicated build configurations for UI tests. And down the rabbit hole we go. But if you are in the situation, this would be an option. After installing the NuGet package, we must enable the testing agent by adding Xamarin.Calabash.Start() to the AppDelegate.cs file: using Foundation; namespace MauiTesting101; [Register("AppDelegate")] public class AppDelegate : MauiUIApplicationDelegate { protected override MauiApp CreateMauiApp() { #if ENABLE_TEST_CLOUD Xamarin.Calabash.Start(); #endif return MauiProgram.CreateMauiApp(); } } You can define the constant as follows in your .NET MAUI app project file: &lt;Project Sdk="Microsoft.NET.Sdk"&gt; &lt;PropertyGroup&gt; ... &lt;PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "&gt; &lt;DefineConstants Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios' OR $([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst' "&gt;TRACE;DEBUG;NET;NET7_0;NETCOREAPP;ENABLE_TEST_CLOUD&lt;/DefineConstants&gt; &lt;DefineConstants Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) != 'ios' AND $([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) != 'maccatalyst' "&gt;TRACE;DEBUG;NET;NET7_0;NETCOREAPP&lt;/DefineConstants&gt; ... &lt;/PropertyGroup&gt; ... &lt;/Project&gt; I had again some issues setting this up. Due to the single project file structure, I added two definitions depending on the platform. There might be a nighter way; let me know what I’m missing out on. 😆 With all this in place, we can now execute the same test for iOS. When should you write UI Tests? As I said in the beginning, UI tests are great at first glance. The end user sees what is being tested, and you write tests very closely to how a real user would interact with the application. However, there are some caveats when writing UI tests, so many do not even try to write them. So, let’s start with the three concerns I hear most: UI tests are slow UI tests are brittle UI tests require high maintenance Performance Right out of the gate, yes, UI tests are slow. If we compare the execution time of writing the above test as a unit test, we compare these two times to each other (run on a MacBook Pro M2Max): UI Test: 9086 ms Unit test: 7 ms There is one significant takeaway from the times you see above. Do not replace your unit tests using UI tests. Also, if all you are checking could be done in a unit test, then go with the unit test. UI tests are not meant to replace a unit test. They are intended to allow you to test the UI layer without having to go over a test script on every release (you do have a test script, right? 😉) One strategy to improve performance on your UI tests, apart from ensuring they add value by testing the UI layer, is to parallelize the execution of your tests. This will reduce the time it takes until you get the feedback from an automated build that includes UI tests. Flaky UI tests Most UI test setups test the entire code base. They are used to test the UI and whatever service is invoked by UI actions. It will execute said code chain from the UI, ViewModel, Service and sometimes even the backend. So, if anything goes wrong, it could be at any stage. A test that appears to be randomly failing or succeeding is terrible since you will lose confidence when it is read that there is a problem you should actually be investigating. So, have different tests to catch errors in separate layers. The UI test can be a great canary, but it often will not be able to pinpoint the source of the issue. Another reason UI tests can fail is that some action takes too long. There can be many reasons, from slow devices, slow network, overloaded test server, etc. - long story short, this can be the most unnerving because everything is correct, and it is really tough to figure out why the app is running so slow. One obvious solution is cranking up the default timeout. Still, it can also be a great starting place to think about improving your app’s performance to ensure the user experience does not take a hit due to a bad performance. UI tests require high maintenance Writing a good UI test takes more thought than your average unit/integration test. There are more moving parts, and anyone could fail. It is now up to you if that failure should indicate the app has failed or if there was some glitch that should be caught by the user/test code. Another reason UI Tests take longer is whenever the UI has some considerable change (redesign). It might require you to rewrite the UI tests. How the UI tests interact with your app also means you must execute the test to determine if it is still working. Is it worth the trouble As you can see, UI tests are no silver bullet. But they provide a unique opportunity to validate that your app is running as it should. I like the idea of an automated smoke test of an app. This test checks if the app starts up as expected. The next series of tests validate that every screen opens without issue in the app. These tests usually take little to write and maintain. The next step would be to test an app’s most common scenarios. This usually eliminates the check every screen test. Writing these tests is more complex and will require additional thought when creating them. Generally speaking, I would strive to test the happy path. The maintenance will be higher and should be considered when choosing which tests to perform using UI tests. Conclusion You can actually write UI tests for your .NET MAUI app. Yes, there are still some rough edges here and there to get it running. But it does run. The nice thing about the UI tests is that they follow the spirit of writing once and then having it run on multiple platforms. You do not have to implement tests for every platform your app runs on. Currently, the tests are limited to iOS, Mac Catatalyst and Android. As stated above, it is to be seen how this goes forward. When used appropriately, UI tests can be a great addition to your other tests and be a real time and money saver. The only alternative to automated UI tests is manual or exploratory field testing, usually a combination of bug reports by your users and (sound) log files to find the error and have an update ready as quickly as possible once a problematic bug has been discovered. Kudos to Gerald for creating the YouTube video that inspired this post. 🙂 HTH]]></summary></entry><entry><title type="html">Using Reactive UI in your .NET MAUI app</title><link href="https://mallibone.com/post/rxui-maui-101" rel="alternate" type="text/html" title="Using Reactive UI in your .NET MAUI app" /><published>2023-04-25T00:03:00+00:00</published><updated>2023-04-25T00:03:00+00:00</updated><id>https://mallibone.com/post/rxui-maui-101</id><author><name>Mark Allibone</name></author><category term="ReactiveUI" /><category term="Rx" /><category term=".NET MAUI" /><summary type="html"><![CDATA[One of my favourite MVVM framework libraries for C# is Reactive UI. But is it ready for prime time when developing a .NET MAUI app? Spoiler: It is. So let’s look at how to get Reactive UI setup in a .NET MAUI app and how the basics are implemented in Reactive UI. We are talking bindings, commands and wiring up the dependency injection. We will rewrite the Hello World .NET MAUI project using Reactive UI in this post. Even add a little timer, aka scheduler sample. I assume you are familiar with the basic MVVM concept. If not, there is an introduction by Microsoft here. Setup After creating a new .NET MAUI project. Add the following NuGet packages: Reactive UI .NET MAUI Reactive UI Fody Now we can start pushing the logic from the View into the ViewModel. Writing a ViewModel When using Reactive UI the View Model inherits from ReactiveObject. To move the basic functionality of the Hello World application, we will implement a property that we will bind to the button text. A counter value to keep track of the count. And a Command property to handle when the button is selected. Let’s start with the properties which afterwards can be used in the View via binding: public class MainViewModel : ReactiveObject, IActivatableViewModel { public MainViewModel() { // Configuration of CounterButtonText and ButtonClickedCommand } [Reactive] public int Count { get; set; } [ObservableAsProperty] public string CounterButtonText { get; } public ReactiveCommand&lt;Unit,Unit&gt; ButtonClickedCommand { get; } // ... } Reactive UI Fody provides the attributes [Reactive] and [ObservableAsProperty]. Which reduces the amount of typing required to define bindable properties. You can read more on the official docs. The Count is a property with a getter and setter and can be used like a normal Property in C#. The CounterButtonText is a bit special. When we increment the counter we want to change the button text. Since our Counter property raises an event everytime it gets changed. Which means we can write a Observable as Property and implement the button text changes: Reactive UI Fody provides the attributes [Reactive] and [ObservableAsProperty]. The attributes reduce the amount of typing required to define bindable properties. More info can be found in the official docs. The Count is a property with a getter and setter and can be used like a typical Property in C#. The CounterButtonText is special. Since it will never be set by the UI. A Read Only Property can be used. When the Count has a new value. The button text is changed. The Counter property raises an event every time it gets changed. This event can be observed by using Observable as Property; the text can be updated: this.WhenAnyValue(vm =&gt; vm.Count) .Select(c =&gt; c switch { 0 =&gt; "Click me", 1 =&gt; "Clicked 1 time", _ =&gt; $"Clicked {c} times" }) .ToPropertyEx(this, vm =&gt; vm.CounterButtonText); Implementing the (synchronous) Command is done with the ReactiveCommand helpers from ReactiveUI: ButtonClickedCommand = ReactiveCommand.Create(ButtonClicked); With all these pieces in place, we are feature complete with the original hello world sample using the MVVM architectural pattern. MVVM is a proven way to write extensible and maintainable front-end code. But using a library like Reactive UI provides additional features. Features are helpful in common front-end scenarios. For instance, having a timer that periodically checks/changes a value. The standard “Hello World” message could use some improvement. Let’s have it display a reactish Hello World message. Let’s start by adding a new property to hold the displayed text: [Reactive] public string Greeting { get; set; } With the property in place - the timer and the logic to change the text every second can be implemented: public class MainViewModel : ReactiveObject, IActivatableViewModel { public MainViewModel() { this.WhenActivated(disposables =&gt; { // ... Observable .Timer( TimeSpan.FromMilliseconds(100), // give the view time to activate TimeSpan.FromMilliseconds(1000), RxApp.MainThreadScheduler) .Do( t =&gt; { var newGreeting = $"Hello, {Traits[t % Traits.Length]} world !"; Console.WriteLine( $"[vm {Thread.CurrentThread.ManagedThreadId}]: " + $"Timer Observable -&gt; " + $"Setting greeting to: \"{newGreeting}\""); Greeting = newGreeting; }, () =&gt; Console.WriteLine( "Those are all the greetings, folks! " + "Feel free to close the window now...\n")) .Subscribe() .DisposeWith(disposables); // ... } [Reactive] public string Greeting { get; set; } // More Properties public ViewModelActivator Activator { get; } = new(); } The timer is placed in the WhenActivated extension method. This helper allows starting and disposing of Observables. The default usage is bound to the lifecycle of the View. When the View is being rendered WhenActivated is invoked. When the user navigates away from the View, all the items are disposed of by a CompositeDisposable. As per documentation, the ViewModel inherits from IActivatableViewModel. The View invokes the method by inheriting ReactiveContentPage&lt;T&gt;. For more details, be sure to read the docs. Dependency Injection With all the functionality in place, there is one last thing left to do. Wiring up the ViewModel to the View. While it is possible to do this all by hand, aka BindingContext = new FunkyViewModel(), this can get rather tedious. With .NET MAUI, there is a standard way to register and resolve dependencies automagically. In the MauiProgram.cs file, use the builder to register all our classes, including Views and ViewModels: public static class MauiProgram { public static MauiApp CreateMauiApp() { var builder = MauiApp.CreateBuilder(); // Maui registration stuff builder.Services.AddTransient&lt;MainPage&gt;(); builder.Services.AddScoped&lt;MainViewModel&gt;(); // ... return builder.Build(); } } With this in place, we can change the constructor of our View to get the ViewModel injected: public partial class MainPage : ReactiveContentPage&lt;MainViewModel&gt; { public MainPage(MainViewModel viewModel) { ViewModel = viewModel; InitializeComponent(); this.WhenActivated(_ =&gt; { }); } } Check out the official docs to learn more about how Dependency Injection works in .NET MAUI. ## Conclusion .NET MAUI and Reactive UI allow you to write native applications for desktop and mobile using the well-known and proven patterns of reactive programming using C#. This post looked at many topics concerning integrating Reactive UI into your .NET MAUI application. You can find the complete sample on GitHub using the fundamental functions of Reactive UI, such as Bindings, Commands and timers. HTH]]></summary></entry><entry><title type="html">.NET MAUI Device Runner Unit Tests</title><link href="https://mallibone.com/post/maui-device-unit-testing" rel="alternate" type="text/html" title=".NET MAUI Device Runner Unit Tests" /><published>2023-03-28T00:03:00+00:00</published><updated>2023-03-28T00:03:00+00:00</updated><id>https://mallibone.com/post/maui-device-unit-testing</id><author><name>Mark Allibone</name></author><category term="Automated Testing" /><category term="XUnit" /><category term="Device Runner" /><category term=".NET MAUI" /><summary type="html"><![CDATA[Automated tests can be a great timesaver when developing any kind of application. Some pieces of code can be harder to test automatically since they require a dependency on an external ressource. When writing .NET MAUI apps this often means that a piece of code or library requires to run on a platform to perform it’s function. This is the time when running your code on a device or emulator becomes mandatory. So let’s see how this can be done. Before we start looking at how device running tests are implemented. Let’s quickly discuss when these tests can be of great value. Generally speaking it is often a good idea to write your app in a way that your business logic does not have any dependency on any external parts. In C# this is usually done via Dependency Inversion and Dependency Injection. But if you are writing the code that interacts with a platform, the very reason of existence for that code is to be dependent on a given platform SDK. In those cases using device running unit tests will be a great time saver. App setup The sample we will be using does not require any platform specifics, but it will highlight how we have to architect our app and how to setup the test project. As a test project we will use the Hello World .NET MAUI project and add a ViewModel which takes over the code that is implemented in the code behind. The ViewModel will be placed in a class library and the .NET MAUI app will reference said library. The test project will then reference said library. Giving us the following projects: The ViewModel implements the counter logic and uses the Community Toolkit MVVM library: public partial class MainViewModel : ObservableObject { [ObservableProperty] private int _count; [ObservableProperty] private string _text = "Click me"; [RelayCommand] public void CounterClicked() { Count++; if (Count == 1) Text = $"Clicked {Count} time"; else Text = $"Clicked {Count} times"; } } Setting up the Device Runner Testproject With the projects and code to test in place, lets move along and implement the test project. We start out by creating a new .NET MAUI application and deleting the following files App.xaml, App.xaml.cs, AppShell.xaml, AppShell.xaml.cs, MainPage.xaml, MainPage.xaml.cs . Next we will install the Shiny.Xunit.Runners.Maui NuGet package, created by Allan Ritchie. And change the Program.cs to look as follows. public static class MauiProgram { public static MauiApp CreateMauiApp() =&gt; MauiApp .CreateBuilder() .ConfigureTests(new TestOptions() { Assemblies = { typeof(MauiProgram).Assembly } }) .UseVisualRunner() .Build(); } With this our Device Runner Testsetup is complete and we can go ahead and implement a test: public class MainViewModelShould { [Fact] public void IncrementcountOnCounterClicked() { // Arrange var sut = new MainViewModel(); // Act sut.CounterClickedCommand.Execute(null); // Assert Assert.Equal(1, sut.Count); } } With that done all that is left is to execute the device runner project and tap the button to run the tests. Conclusion To run device tests requires some adaptation how a .NET MAUI project needs to be setup. By extracting the business logic we can reference it in our Device Runner MAUI project. The tests are based on the XUnit Testing framework. You might wonder. Should you run all your tests from now on using device runners? Short answer probably not. While running your tests on a device will also check your logic. It will further allow you to see how well code performs on a device. But even though you can get these additional insights, it takes longer to execute. Plus running the tests always involves waiting for the device/emulator to start and then tap a button. Therefore I would recommend to use device runners when testing code that requires running against a device sdk, or to explicitly test if your code is able to run on a specific test device. You can find the complete sample on GitHub. HTH]]></summary></entry><entry><title type="html">Writing Unit Tests for your .NET MAUI app</title><link href="https://mallibone.com/post/maui-unit-testing" rel="alternate" type="text/html" title="Writing Unit Tests for your .NET MAUI app" /><published>2023-03-14T00:03:00+00:00</published><updated>2023-03-14T00:03:00+00:00</updated><id>https://mallibone.com/post/maui-unit-testing</id><author><name>Mark Allibone</name></author><category term="Automated Testing" /><category term="XUnit" /><category term=".NET MAUI" /><summary type="html"><![CDATA[Writing mobile apps usually brings along its fair share of application logic run on the device. When writing code, it is paramount to test it. How else will you know if it is working as expected? The answer is testing. And since we are developers, we like to automate mundane tasks. So Unit Testing our logic can be a great way to test our logic. If you know how to set up unit tests, skip to the TL;DR; marker. 🙃 Unit Tests allow us to test the logic parts of our app. This will enable us to try most of our apps except the views. The most popular testing frameworks to the date of writing this post are XUnit, NUnit and MS Test. All three are solid choices and mostly have the same functionality. They mainly differ in how tests are written. For my Unit Tests, I usually like to go with XUnit. Ensure that the .NET MAUI and test projects use the same .NET version. In this example, it will be .NET 7. Given an existing .NET MAUI application, we can add a new testing project to the solution and choose XUnit as the testing framework. To have some code, we can test, I have rewritten the .NET MAUI hello world app to use a ViewModel (using the Community Toolkit MVVM framework). Doing so gives us the following ViewModel code. public partial class MainViewModel : ObservableObject { [ObservableProperty] public int _count; [ObservableProperty] public string _text = "Click me"; [RelayCommand] public void CounterClicked() { Count++; if (Count == 1) Text = $"Clicked {Count} time"; else Text = $"Clicked {Count} times"; } } We can test this logic by writing a Test that calls the command method in the well-known Arrange/Act/Assert pattern: public class MainViewModelShould { [Fact] public void IncrementcountOnCounterClicked() { // Arrange var sut = new MainViewModel(); // Act sut.CounterClickedCommand.Execute(null); // Assert Assert.Equal(1, sut.Count); } } This will require us to add a project reference from the test project to the .NET MAUI project. &lt;Project Sdk="Microsoft.NET.Sdk"&gt; &lt;!-- ... --&gt; &lt;ItemGroup&gt; &lt;ProjectReference Include="..\MauiTesting101\MauiTesting101.csproj" /&gt; &lt;/ItemGroup&gt; &lt;/Project&gt; TL;DR; Up to this point, we just followed the usual steps for setting up a Unit Test project. If you put your app logic in the .NET MAUI app project, you will get a weird error when compiling the test project. To make the compiler happy, we will have to change the .NET MAUI .csproj file as follows: &lt;OutputType Condition="'$(TargetFramework)' != 'net7.0'"&gt;Exe&lt;/OutputType&gt; Note that we have to add a condition to the &lt;OutputType&gt; or else you will get an error that there is no main method when executing the test. If you are using a different .NET version i.e. .NET 6 you would have to write the condition as follows &lt;OutputType Condition="'$(TargetFramework)' != 'net6.0'"&gt;. And for .NET 8 we would have to replace number with an 8 and so forth. Note that we have to add a condition to the &lt;OutputType&gt;, or else you will get an error that there is no primary method when executing the test. If you are using a different .NET version, i.e. .NET 6, you would have to write the condition as follows &lt;OutputType Condition="'$(TargetFramework)' != 'net6.0'"&gt;. And for .NET 8, we would have to replace the number with an 8. And for .NET 9, we … you get the point. 😉 With these changes, we can start running the tests from your IDE or via the command line using dotnet test . You can find the complete example on GitHub. HTH]]></summary></entry><entry><title type="html">EF Core - Creating queries with lists</title><link href="https://mallibone.com/post/efcore-filter-list-query" rel="alternate" type="text/html" title="EF Core - Creating queries with lists" /><published>2022-12-26T10:03:00+00:00</published><updated>2022-12-26T10:03:00+00:00</updated><id>https://mallibone.com/post/efcore-filter-list-query</id><author><name>Mark Allibone</name></author><category term="EF Core" /><summary type="html"><![CDATA[When writing queries towards the database at some point the need arises to filter out all items that are within a collection. For example let’s say we have a list of Ids and want to get all the people associated with such an Id. How can we do this using EF Core? And what are the pitfalls that lead to the motivation of writing this blogpost? 🙈 If we had a list of attendee Ids with three Ids (123, 456, 789) and would want to find all the people having such an Id, the following SQL query (for an MS SQL DB) would yield the right result: SELECT * FROM People WHERE AttendeeId IN (123, 456, 789) Now don’t hung up on the SELECT * in fact don’t use it in production code. But the point of the SQL sample above is that with IN we can filter for a list of Arguments. If we want EF Core to generate this we have to write the following LINQ statement: List&lt;Person&gt; attendees = await _context.People.Where(p =&gt; attendeeIds.Contains(p.AttendeeId)).ToListAsync(); If we inspect this query by using the ToQueryString() we see that this statement is converted into a SQL statement. SELECT [p].[PersonId], [p].[AttendeeId], [p].[Name] FROM [People] AS [p] WHERE [p].[AttendeeId] IN (1, 2, 3) As the query above shows, using attendeeIds.Contains(p =&gt; p.AttendeeId) is correctly translated into an SQL query using an IN statement. Update: Erik has brought to my attention, that there are limits to this approach. If you have a large set of Ids this approach will fall short. For example the number of parameters for a SQL Server Query is limited to 2100 parameters per call. If you have larger sets of Ids be sure to check out this Gist and if you are using SQL Server 2016 or later check out this Post. Thanks Erik for pointing this out. 🙂 Potential Pitfalls Client Evaluation: There are other ways in LINQ how we could write this filter, and EF Core might even go ahead and execute it but it would most probably be executed by the application. In general executing queries in the application is not desired. SQL database engines are way more efficient in handling these queries. Be sure to consult the MS Learn on Client Evaluation to get a better understanding. Not using List: I have a strong tendency to write functional inspired C#. So I very rarely pass a list into a function and prefer to pass in an IReadOnlyCollection . For the above statement to be generated by EF Core you must use a collection of type List or else you could end up with a runtime exception. Wrap up Implementing a Filter using a list with EF Core is thanks to LINQ pretty straight forward. There are some pitfalls though which have to be kept in mind. Since EF Core has to generate an SQL Query from the LINQ statement there can be cases that the LINQ query will not be translated into a SQL. When this happens EF Core will fallback to Client Evaluation, which Filtering Data in a SQL Table using EF Core takes some inside knowledge but is not complicated. HTH]]></summary></entry></feed>