mu882024-01-26T13:18:24+00:00mu88mu88+devblog@posteo.euUsing .NET SDK Container Building Tools2024-01-21T00:00:00+00:00/2024/01/21/SDK-container-building-tools<p>With every new release of .NET at the end of the year comes a bunch of new language and framework features, but usually some handy SDK features as well. One of them are the <em>.NET SDK Container Building Tools</em>.</p>
<p>If you’ve never heard of them before: when it comes to containerizing a .NET app, most IDEs support creating <code class="language-plaintext highlighter-rouge">Dockerfile</code>s from an existing .NET project.<br />
In Rider, for example, adding Docker support is as simple as this which puts all the necessary things together:</p>
<p><img src="/public/post_assets/2024-01-21-SDK-container-building-tools/Image_1.png" alt="" /></p>
<p>While this is nice and good, with the rise of smaller and smaller projects and therefore even simpler <code class="language-plaintext highlighter-rouge">Dockerfile</code>s, having a dedicated <code class="language-plaintext highlighter-rouge">Dockerfile</code> with a lot of boilerplate code became more and more an unnecessary overhead.<br />
Thankfully, the .NET team at Microsoft recently added support for building container images to the SDK 🥳</p>
<h2 id="build-container-image-using-dotnet-publish">Build container image using <code class="language-plaintext highlighter-rouge">dotnet publish</code></h2>
<p>The default tool to publish an app is <code class="language-plaintext highlighter-rouge">dotnet publish</code>. Therefore, the container building support has been added to this tool as well. Let’s see how this works.</p>
<p>First of all, this is our <code class="language-plaintext highlighter-rouge">Dockerfile</code> to migrate:</p>
<figure class="highlight"><pre><code class="language-dockerfile" data-lang="dockerfile"><span class="k">ARG</span><span class="s"> BASE_IMAGE=mcr.microsoft.com/dotnet/aspnet:8.0</span>
<span class="k">FROM</span><span class="s"> ${BASE_IMAGE} AS base</span>
<span class="k">WORKDIR</span><span class="s"> /app</span>
<span class="k">FROM</span><span class="s"> mcr.microsoft.com/dotnet/sdk:8.0 AS build</span>
<span class="k">WORKDIR</span><span class="s"> /src</span>
<span class="k">COPY</span><span class="s"> ["MyProject/MyProject.csproj", "MyProject/"]</span>
<span class="k">RUN </span>dotnet restore <span class="s2">"MyProject/MyProject.csproj"</span>
<span class="k">COPY</span><span class="s"> . .</span>
<span class="k">WORKDIR</span><span class="s"> "/src/MyProject"</span>
<span class="k">RUN </span>dotnet build <span class="s2">"MyProject.csproj"</span> <span class="nt">-c</span> Release <span class="nt">-o</span> /app/build
<span class="k">FROM</span><span class="s"> build AS publish</span>
<span class="k">RUN </span>dotnet publish <span class="s2">"MyProject.csproj"</span> <span class="nt">-c</span> Release <span class="nt">-o</span> /app/publish
<span class="k">FROM</span><span class="s"> base AS final</span>
<span class="k">WORKDIR</span><span class="s"> /app</span>
<span class="k">COPY</span><span class="s"> --from=publish /app/publish .</span>
<span class="k">ENTRYPOINT</span><span class="s"> ["dotnet", "MyProject.dll"]</span></code></pre></figure>
<p>As you can see, it is a regular multi-stage build for an ASP.NET Core app.</p>
<p>The project file <code class="language-plaintext highlighter-rouge">MyProject.csproj</code> is even simpler:</p>
<figure class="highlight"><pre><code class="language-xml" data-lang="xml"><span class="nt"><Project</span> <span class="na">Sdk=</span><span class="s">"Microsoft.NET.Sdk.Web"</span><span class="nt">></span>
<span class="nt"><PropertyGroup></span>
<span class="nt"><TargetFramework></span>net8.0<span class="nt"></TargetFramework></span>
<span class="nt"><DockerDefaultTargetOS></span>Linux<span class="nt"></DockerDefaultTargetOS></span>
<span class="nt"></PropertyGroup></span>
<span class="nt"><ItemGroup></span>
<span class="nt"><Content</span> <span class="na">Update=</span><span class="s">"appsettings.json"</span><span class="nt">></span>
<span class="nt"><CopyToOutputDirectory></span>Always<span class="nt"></CopyToOutputDirectory></span>
<span class="nt"></Content></span>
<span class="nt"></ItemGroup></span>
<span class="nt"></Project></span></code></pre></figure>
<p>Now all we have to do is to ensure that Docker is running and call <code class="language-plaintext highlighter-rouge">dotnet publish --os linux --arch x64 /t:PublishContainer -c Release</code>. This will do the following:</p>
<ul>
<li>Build and publish the app in <code class="language-plaintext highlighter-rouge">Release</code> mode.</li>
<li>Build a Docker image for Linux, targetting the <code class="language-plaintext highlighter-rouge">x64</code> platform, and add it to the local Docker registry.</li>
</ul>
<p>And that’s it, now we can throw away the <code class="language-plaintext highlighter-rouge">Dockerfile</code> 🚮</p>
<h2 id="publishing-the-image-to-docker-hub">Publishing the image to Docker Hub</h2>
<p>As mentioned before, the created image will be added to the local Docker registry. But <code class="language-plaintext highlighter-rouge">dotnet publish</code> can also push the image to a container registry like Docker Hub.</p>
<p>For this, the MSBuild properties <code class="language-plaintext highlighter-rouge">ContainerRegistry</code> and <code class="language-plaintext highlighter-rouge">ContainerRepository</code> must be specified, either via command line or within <code class="language-plaintext highlighter-rouge">MyProject.csproj</code>. My default is to add <code class="language-plaintext highlighter-rouge">ContainerRepository</code> to the project file (to ensure a properly named image even in the local registry) and only add <code class="language-plaintext highlighter-rouge">ContainerRegistry</code> via command line when necessary (because when testing locally, I don’t always want to push the newly created image to Docker Hub), so it looks like this:</p>
<figure class="highlight"><pre><code class="language-xml" data-lang="xml"><span class="nt"><Project</span> <span class="na">Sdk=</span><span class="s">"Microsoft.NET.Sdk.Web"</span><span class="nt">></span>
<span class="nt"><PropertyGroup></span>
<span class="nt"><TargetFramework></span>net8.0<span class="nt"></TargetFramework></span>
<span class="nt"><DockerDefaultTargetOS></span>Linux<span class="nt"></DockerDefaultTargetOS></span>
<span class="nt"><ContainerRepository></span>mu88/myproject<span class="nt"></ContainerRepository></span>
<span class="nt"></PropertyGroup></span>
<span class="nt"><ItemGroup></span>
<span class="nt"><Content</span> <span class="na">Update=</span><span class="s">"appsettings.json"</span><span class="nt">></span>
<span class="nt"><CopyToOutputDirectory></span>Always<span class="nt"></CopyToOutputDirectory></span>
<span class="nt"></Content></span>
<span class="nt"></ItemGroup></span>
<span class="nt"></Project></span></code></pre></figure>
<p>Now when calling <code class="language-plaintext highlighter-rouge">dotnet publish --os linux --arch x64 /t:PublishContainer -c Release -p:ContainerRegistry=registry.hub.docker.com</code>, the .NET SDK will try to push the image to Docker Hub and… fail 💥<br />
The reason is simple: as Docker Hub requires authentication to upload images, <code class="language-plaintext highlighter-rouge">docker login</code> must be called before. After doing so, the command runs nicely.</p>
<h2 id="further-configuration">Further configuration</h2>
<p>As you can read in <a href="https://learn.microsoft.com/en-us/dotnet/core/docker/publish-as-container">the official Microsoft docs</a>, there are several other options to configure the process of image creation. I want to highlight <code class="language-plaintext highlighter-rouge">ContainerImageTag(s)</code> and <code class="language-plaintext highlighter-rouge">ContainerFamily</code>.</p>
<p>Using the property <code class="language-plaintext highlighter-rouge">ContainerImageTag(s)</code>, several Docker image tags can be specified. So for example, we can add the following to <code class="language-plaintext highlighter-rouge">MyProject.csproj</code>:</p>
<figure class="highlight"><pre><code class="language-xml" data-lang="xml"><span class="nt"><PropertyGroup></span>
<span class="nt"><ContainerImageTag></span>dev<span class="nt"></ContainerImageTag></span>
<span class="nt"></PropertyGroup></span></code></pre></figure>
<p>This way, all locally built images will always receive the tag <code class="language-plaintext highlighter-rouge">dev</code>. When pushing the images to Docker Hub, the tags can be overridden dynamically via MSBuild: <code class="language-plaintext highlighter-rouge">dotnet publish --os linux --arch x64 /t:PublishContainer -c Release -p:ContainerRegistry=registry.hub.docker.com '-p:ContainerImageTags="1.0.0;latest"'</code></p>
<p>This will lead to a nicely tagged image using <code class="language-plaintext highlighter-rouge">1.0.0</code> and <code class="language-plaintext highlighter-rouge">latest</code> on Docker Hub.</p>
<p>Unfortunately, it is not yet supported to build multi-platform images (upvote <a href="https://github.com/dotnet/sdk-container-builds/issues/87">this GitHub issue</a> 🙏🏻). So while it is possible to build a single image which supports e. g. both <code class="language-plaintext highlighter-rouge">arm64</code> and <code class="language-plaintext highlighter-rouge">x64</code> which the native Docker pipeline, this cannot be achieved yet with the .NET SDK Container Building tools. Instead, the <code class="language-plaintext highlighter-rouge">dotnet publish</code> tool has to be called twice, once per platform, but also with different tags:</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell">dotnet publish <span class="nt">--os</span> linux <span class="nt">--arch</span> arm64 /t:PublishContainer <span class="nt">-c</span> Release <span class="nt">-p</span>:ContainerRegistry<span class="o">=</span>registry.hub.docker.com <span class="s1">'-p:ContainerImageTags="1.0.0-arm64;latest-arm64"'</span>
dotnet publish <span class="nt">--os</span> linux <span class="nt">--arch</span> x64 /t:PublishContainer <span class="nt">-c</span> Release <span class="nt">-p</span>:ContainerRegistry<span class="o">=</span>registry.hub.docker.com <span class="s1">'-p:ContainerImageTags="1.0.0-x64;latest-x64"'</span></code></pre></figure>
<p>The property <code class="language-plaintext highlighter-rouge">ContainerFamily</code> is extremely handy to easily change the base image used to build and publish the app. For example, by adding <code class="language-plaintext highlighter-rouge">p:ContainerFamily=jammy-chiseled</code> one can easily build an image using the new chiseled base images (<a href="https://devblogs.microsoft.com/dotnet/announcing-dotnet-chiseled-containers/">see official Microsoft blog post for more information</a>). We will look at this in a later blog post.</p>
<h2 id="closing">Closing</h2>
<p>Now that we saw the new tools in action, the big question is: <em>why should we use them, what’s the problem using a dedicated <code class="language-plaintext highlighter-rouge">Dockerfile</code>?</em> And this question is absolutely valid. I personally see the following benefits:</p>
<ol>
<li>Less files, less clutter, less maintenance → while it’s only a marginal benefit, I’m always happy if I can remove unneeded things, especially when it comes to boilerplate code.</li>
<li>More coherent project structure → before, it was possible to have different versions of .NET specified in <code class="language-plaintext highlighter-rouge">*.csproj</code> and <code class="language-plaintext highlighter-rouge">Dockerfile</code>, either by intention or accident. I don’t like it when information that belongs together is spread over several files.</li>
</ol>
<p>The second point becomes even more handy when updating to a new major version of .NET: until now, my dependency update tool Renovate Bot automatically filed a PR with the latest version of .NET for the <code class="language-plaintext highlighter-rouge">Dockerfile</code>, but without modifying the <code class="language-plaintext highlighter-rouge">*.csproj</code>, leading to a mix of different versions. Which the new approach using the SDK Container Building Tools, this “problems” simply vanishes.</p>
<p>Of course, there are situations when the process of building a Docker image has to be tweaked and then the <code class="language-plaintext highlighter-rouge">Dockerfile</code> remains the way to go. But it’s really nice to see the SDK improving with year by year, not only C# and the .NET runtime.</p>
<p>I hope you enjoyed reading the first post in 2024 - take care 👋🏻</p>
Structural Search and Replace2023-07-31T00:00:00+00:00/2023/07/31/Structural-Search-Replace<p>Last week, I implemented the following little extension method to make the conversion between <code class="language-plaintext highlighter-rouge">DateTime</code> and <code class="language-plaintext highlighter-rouge">DateOnly</code> more concise:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>
<span class="k">namespace</span> <span class="nn">My.Extensions</span><span class="p">;</span>
<span class="k">public</span> <span class="k">static</span> <span class="k">class</span> <span class="nc">DateTimeExtensions</span>
<span class="p">{</span>
<span class="c1">// ReSharper disable once replace.dateonly.fromdatetime.by.todateonly</span>
<span class="k">public</span> <span class="k">static</span> <span class="n">DateOnly</span> <span class="nf">ToDateOnly</span><span class="p">(</span><span class="k">this</span> <span class="n">DateTime</span> <span class="n">dateTime</span><span class="p">)</span> <span class="p">=></span> <span class="n">DateOnly</span><span class="p">.</span><span class="nf">FromDateTime</span><span class="p">(</span><span class="n">dateTime</span><span class="p">);</span>
<span class="p">}</span></code></pre></figure>
<p>Especially in automated tests, I love using the <a href="https://fluentassertions.com/datetimespans/">Fluent Assertions for dates and times</a> because it makes the following more readable:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="c1">// old</span>
<span class="kt">var</span> <span class="n">dateOnly</span> <span class="p">=</span> <span class="n">DateOnly</span><span class="p">.</span><span class="nf">FromDateTime</span><span class="p">(</span><span class="m">12.</span><span class="nf">April</span><span class="p">(</span><span class="m">1953</span><span class="p">));</span>
<span class="c1">// new</span>
<span class="kt">var</span> <span class="n">dateOnly</span> <span class="p">=</span> <span class="m">12.</span><span class="nf">April</span><span class="p">(</span><span class="m">1953</span><span class="p">).</span><span class="nf">ToDateOnly</span><span class="p">();</span></code></pre></figure>
<p>Aside this little extension method and the corresponding changes, I added a <em>ReSharper Search Pattern</em> to the pull request for automatically suggesting to use the extension method:</p>
<p><img src="/public/post_assets/2023-07-31-Structural-Search-Replace/Image_1.png" alt="" /></p>
<p>Since my fellow reviewing colleague was not aware of this feature, I decided to dedicate it a blog post.</p>
<h2 id="structural-search-and-replace">Structural Search and Replace</h2>
<p>This is another member out of the collection <em>tiny but powerful ReSharper features many people are not aware of</em>. As usual, the <a href="https://www.jetbrains.com/help/resharper/Navigation_and_Search__Structural_Search_and_Replace.html">official JetBrains docs</a> are pretty extensive, but put in my own very few words: this feature allows you to search your code not only textual but syntactical for certain patterns (e. g. with exactly one argument of a given type).</p>
<p>Not only can you search for those patterns, you can define replacement patterns and a custom severity - in my case it’s highlighted as a <em>Suggestion</em>.</p>
<p>And last but not least, these refactorings are saved within the typical ReSharper <code class="language-plaintext highlighter-rouge">*.DotSettings</code> file, making it possible to keep your personal collection of refactorings or share them with you fellow colleagues via <code class="language-plaintext highlighter-rouge">YourSolution.sln.DotSettings</code>.<br />
It is a very lightweight approach, especially for such relatively small refactorings, and is therefore superior to a Roslyn Analyser because I don’t like creating another assembly just for such a one-liner.</p>
<h2 id="how-to-configure">How to configure</h2>
<p>At the moment of writing, there is no way to configure this in Rider (<a href="https://youtrack.jetbrains.com/issue/RIDER-11489/Implement-UI-for-Structural-Search-and-Replace-SSR">please upvote this JetBrains issue</a>) - so it’s one of those rare times when I open Visual Studio, load the Solution that I want the refactoring to apply to and navigate to <em>ReSharper | Options | Code Inspection | Custom Patterns</em>.</p>
<p>For example, my previously shown refactoring looks like this:</p>
<p><img src="/public/post_assets/2023-07-31-Structural-Search-Replace/Image_2.png" alt="" /></p>
<p>Note that by specifying a <em>Suppression key</em>, you can even instruct ReSharper to ignore a particular finding.</p>
<p>As you might have noticed, I’ve added the following comment to the extension method:<br />
<code class="language-plaintext highlighter-rouge">// ReSharper disable once replace.dateonly.fromdatetime.by.todateonly</code><br />
This way ReSharper doesn’t suggest to use my extension method here as well, because otherwise it would end with the following recursive path:</p>
<p><img src="/public/post_assets/2023-07-31-Structural-Search-Replace/Image_3.png" alt="" /></p>
<p>After saving the pattern to the corresponding layer, we’re ready to go 💪🏻 no need for Visual Studio anymore, so it can be closed and after opening the Solution again in Rider, the new refactoring hint becomes available. Finally, don’t forget to create a pull request to share it with your colleagues.</p>
<h2 id="closing">Closing</h2>
<p>I hope I could show you another little helper for your tool belt that you can use the next time when a dedicated Roslyn analyzer feels just too much.</p>
<p>Thank you for reading and take care!</p>
Analyzing the EF Core Query Cache2023-03-13T00:00:00+00:00/2023/03/13/EF-Core-query-cache<p>As I mentioned a couple of times, we at Swiss Post strive for a Cloud-first approach and embrace various techniques and tools to make that happen. But sometimes, these tools can guide you into a completely wrong direction if not understood properly - as it happened to me last week 😅</p>
<h2 id="heres-what-happened">Here’s what happened</h2>
<p>All of our applications are Docker-ized and run on K8s. To get an easy overview of the applications health, .NET metrics are collected and visualized in Grafana. Here’s what a typical Grafana dashboard looks like for one of our applications:<br />
<img src="/public/post_assets/2023-03-13-EF-Core-query-cache/Image_1.png" alt="" /></p>
<p>It contains a lot of <a href="https://learn.microsoft.com/en-us/dotnet/core/diagnostics/available-counters">.NET <code class="language-plaintext highlighter-rouge">EventCounter</code>s that you can find here</a> and we do have metrics for EF Core as well:<br />
<img src="/public/post_assets/2023-03-13-EF-Core-query-cache/Image_2.png" alt="" /></p>
<p>In the lower left corner of the previous screenshot, you see the <em>Compiled Query</em> metric, referring to the <em>EF Core Query Cache Hit Rate</em> which is specified as following (<a href="https://learn.microsoft.com/en-us/ef/core/logging-events-diagnostics/event-counters?tabs=windows#counters-and-their-meaning">taken from here</a>):</p>
<blockquote>
<p>The ratio of query cache hits to misses. The first time a given LINQ query is executed by EF Core (excluding parameters), it must be compiled in what is a relatively heavy process. In a normal application, all queries are reused, and the query cache hit rate should be stable at 100% after an initial warmup period. If this number is less than 100% over time, you may experience degraded perf due to repeated compilations, which could be a result of suboptimal dynamic query generation.</p>
</blockquote>
<p>Well, I think it’s safe to say that 21.5% is not very close to 100% 😉 since the docs do not state how to analyze this kind of problem, <a href="https://stackoverflow.com/questions/75649106/analyze-ef-core-query-cache">I asked on Stack Overflow</a>, giving me some very helpful feedback.</p>
<h2 id="log-query-compilation">Log query compilation</h2>
<p>EF Core has a ton of additional log statements which are not enabled by default. One of this is <code class="language-plaintext highlighter-rouge">CoreEventId.QueryCompilationStarting</code> which gets logged when an EF Core query needs to be (re)compiled.</p>
<p>I’ve enabled the log statement by adding the following line when configuring EF Core:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="n">dbContextOptionsBuilder</span>
<span class="p">.</span><span class="nf">ConfigureWarnings</span><span class="p">(</span><span class="n">warning</span> <span class="p">=></span> <span class="n">warning</span><span class="p">.</span><span class="nf">Log</span><span class="p">(</span><span class="n">CoreEventId</span><span class="p">.</span><span class="n">QueryCompilationStarting</span><span class="p">,</span> <span class="n">LogLevel</span><span class="p">.</span><span class="n">Information</span><span class="p">));</span></code></pre></figure>
<p>Since it’s an <code class="language-plaintext highlighter-rouge">Info</code> log, <code class="language-plaintext highlighter-rouge">appsettings.json</code> needs to be updated as well:</p>
<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="p">{</span><span class="w">
</span><span class="nl">"Logging"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"LogLevel"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"Microsoft.EntityFrameworkCore.Query"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Information"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span></code></pre></figure>
<p>When deploying this change, the following log statements appeared in our logging platform Splunk:<br />
<img src="/public/post_assets/2023-03-13-EF-Core-query-cache/Image_3.png" alt="" /></p>
<p>To obtain a meaningful result, I left the application alone overnight and looked at the logs the next morning. I’d expected certain queries to be recompiled over and over again, causing the bad <em>EF Core Query Cache Hit Rate</em> metric. But to my surprise, all queries were compiled only once, so I had to go one step further.</p>
<h2 id="custom-icompiledquerycache">Custom <code class="language-plaintext highlighter-rouge">ICompiledQueryCache</code></h2>
<p>Another hint I received on Stack Overflow was to implement my own query cache and see what’s going on inside. I just copy and pasted the <a href="https://github.com/dotnet/efcore/blob/release%2F7.0/src/EFCore/Query/Internal/CompiledQueryCache.cs">original sources from GitHub</a> and extended it with some log statements:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">public</span> <span class="k">class</span> <span class="nc">LoggingCompiledQueryCache</span> <span class="p">:</span> <span class="n">ICompiledQueryCache</span>
<span class="p">{</span>
<span class="k">private</span> <span class="k">readonly</span> <span class="n">ILogger</span><span class="p"><</span><span class="n">LoggingCompiledQueryCache</span><span class="p">></span> <span class="n">_logger</span><span class="p">;</span>
<span class="k">public</span> <span class="nf">LoggingCompiledQueryCache</span><span class="p">(</span><span class="n">IMemoryCache</span> <span class="n">memoryCache</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_memoryCache</span> <span class="p">=</span> <span class="n">memoryCache</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">ILoggerFactory</span> <span class="n">loggerFactory</span> <span class="p">=</span> <span class="n">LoggerFactory</span><span class="p">.</span><span class="nf">Create</span><span class="p">(</span><span class="n">builder</span> <span class="p">=></span> <span class="n">builder</span><span class="p">.</span><span class="nf">AddConsole</span><span class="p">());</span>
<span class="n">_logger</span> <span class="p">=</span> <span class="n">loggerFactory</span><span class="p">.</span><span class="n">CreateLogger</span><span class="p"><</span><span class="n">LoggingCompiledQueryCache</span><span class="p">>();</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">virtual</span> <span class="n">Func</span><span class="p"><</span><span class="n">QueryContext</span><span class="p">,</span> <span class="n">TResult</span><span class="p">></span> <span class="n">GetOrAddQuery</span><span class="p"><</span><span class="n">TResult</span><span class="p">>(</span><span class="kt">object</span> <span class="n">cacheKey</span><span class="p">,</span> <span class="n">Func</span><span class="p"><</span><span class="n">Func</span><span class="p"><</span><span class="n">QueryContext</span><span class="p">,</span> <span class="n">TResult</span><span class="p">>></span> <span class="n">compiler</span><span class="p">)</span>
<span class="p">{</span>
<span class="c1">// code left out for brevity</span>
<span class="k">try</span>
<span class="p">{</span>
<span class="k">lock</span> <span class="p">(</span><span class="n">compilationLock</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_memoryCache</span><span class="p">.</span><span class="nf">TryGetValue</span><span class="p">(</span><span class="n">cacheKey</span><span class="p">,</span> <span class="k">out</span> <span class="n">compiledQuery</span><span class="p">))</span>
<span class="p">{</span>
<span class="n">EntityFrameworkEventSource</span><span class="p">.</span><span class="n">Log</span><span class="p">.</span><span class="nf">CompiledQueryCacheHit</span><span class="p">();</span>
<span class="n">_logger</span><span class="p">.</span><span class="nf">QueryCacheHit</span><span class="p">();</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="n">EntityFrameworkEventSource</span><span class="p">.</span><span class="n">Log</span><span class="p">.</span><span class="nf">CompiledQueryCacheMiss</span><span class="p">();</span>
<span class="n">_logger</span><span class="p">.</span><span class="nf">QueryCacheMiss</span><span class="p">();</span>
<span class="c1">// code left out for brevity</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">compiledQuery</span><span class="p">!;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// code left out for brevity</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p>With these additional logging messages:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">public</span> <span class="k">static</span> <span class="k">partial</span> <span class="k">class</span> <span class="nc">Log</span>
<span class="p">{</span>
<span class="c1">/// <summary></span>
<span class="c1">/// This was a EF Core Query Cache hit.</span>
<span class="c1">/// </summary></span>
<span class="c1">/// <param name="logger">The logger.</param></span>
<span class="p">[</span><span class="nf">LoggerMessage</span><span class="p">(</span>
<span class="n">EventId</span> <span class="p">=</span> <span class="m">50001</span><span class="p">,</span>
<span class="n">Level</span> <span class="p">=</span> <span class="n">LogLevel</span><span class="p">.</span><span class="n">Information</span><span class="p">,</span>
<span class="n">Message</span> <span class="p">=</span> <span class="s">"This was a EF Core Query Cache miss."</span><span class="p">,</span>
<span class="n">SkipEnabledCheck</span> <span class="p">=</span> <span class="k">true</span><span class="p">)]</span>
<span class="k">public</span> <span class="k">static</span> <span class="k">partial</span> <span class="k">void</span> <span class="nf">QueryCacheHit</span><span class="p">(</span><span class="k">this</span> <span class="n">ILogger</span> <span class="n">logger</span><span class="p">);</span>
<span class="c1">/// <summary></span>
<span class="c1">/// This was a EF Core Query Cache miss.</span>
<span class="c1">/// </summary></span>
<span class="c1">/// <param name="logger">The logger.</param></span>
<span class="p">[</span><span class="nf">LoggerMessage</span><span class="p">(</span>
<span class="n">EventId</span> <span class="p">=</span> <span class="m">50002</span><span class="p">,</span>
<span class="n">Level</span> <span class="p">=</span> <span class="n">LogLevel</span><span class="p">.</span><span class="n">Information</span><span class="p">,</span>
<span class="n">Message</span> <span class="p">=</span> <span class="s">"This was a EF Core Query Cache miss."</span><span class="p">,</span>
<span class="n">SkipEnabledCheck</span> <span class="p">=</span> <span class="k">true</span><span class="p">)]</span>
<span class="k">public</span> <span class="k">static</span> <span class="k">partial</span> <span class="k">void</span> <span class="nf">QueryCacheMiss</span><span class="p">(</span><span class="k">this</span> <span class="n">ILogger</span> <span class="n">logger</span><span class="p">);</span>
<span class="p">}</span></code></pre></figure>
<p>Finally, the existing <code class="language-plaintext highlighter-rouge">CompiledQueryCache</code> needs to be replaced with the custom implementation:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="n">dbContextOptionsBuilder</span>
<span class="p">.</span><span class="n">ReplaceService</span><span class="p"><</span><span class="n">ICompiledQueryCache</span><span class="p">,</span> <span class="n">CompiledQueryCache</span><span class="p">,</span> <span class="n">LoggingCompiledQueryCache</span><span class="p">>();</span></code></pre></figure>
<p>Let’s take it apart one by one:</p>
<ul>
<li>The custom <code class="language-plaintext highlighter-rouge">LoggingCompiledQueryCache</code> implements the same behavior as the original <code class="language-plaintext highlighter-rouge">CompiledQueryCache</code>.</li>
<li><code class="language-plaintext highlighter-rouge">LoggingCompiledQueryCache</code> logs each time if the underlying <code class="language-plaintext highlighter-rouge">_memoryCache</code> is either hit or missed via <code class="language-plaintext highlighter-rouge">QueryCacheHit()</code> or <code class="language-plaintext highlighter-rouge">QueryCacheMiss()</code>.</li>
</ul>
<p>With these information at hand, I could calculate my own query cache hit rate in Splunk:<br />
<img src="/public/post_assets/2023-03-13-EF-Core-query-cache/Image_4.png" alt="" /></p>
<p>And that was one of these moments when you should leave your notebook, drink a hot cup of coffee and ask yourself why you’ve chosen this job - why is the calculated value 99.9% but Grafana displays 21.5%? 🤯</p>
<h2 id="analyzing-the-metric-in-grafana">Analyzing the metric in Grafana</h2>
<p>When taking a closer look into the data in Grafana, I noticed a lot of of <code class="language-plaintext highlighter-rouge">-1</code> values:<br />
<img src="/public/post_assets/2023-03-13-EF-Core-query-cache/Image_5.png" alt="" /></p>
<p>That felt quite odd to me - where are these values coming from? The answer lies in the <a href="https://github.com/dotnet/efcore/blob/main/src/EFCore/Infrastructure/EntityFrameworkEventSource.cs#L226">EF Core code for calculating this metric</a>:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="kt">var</span> <span class="n">hitsAndMisses</span> <span class="p">=</span> <span class="n">clone</span><span class="p">.</span><span class="n">Hits</span> <span class="p">+</span> <span class="n">clone</span><span class="p">.</span><span class="n">Misses</span><span class="p">;</span>
<span class="c1">// Report -1 for no data to avoid returning NaN, which can trigger issues in downstream consumers</span>
<span class="k">return</span> <span class="n">hitsAndMisses</span> <span class="p">==</span> <span class="m">0</span>
<span class="p">?</span> <span class="p">-</span><span class="m">1</span>
<span class="p">:</span> <span class="p">((</span><span class="kt">double</span><span class="p">)</span><span class="n">clone</span><span class="p">.</span><span class="n">Hits</span> <span class="p">/</span> <span class="n">hitsAndMisses</span><span class="p">)</span> <span class="p">*</span> <span class="m">100</span><span class="p">;</span></code></pre></figure>
<p>If the cache hasn’t been hit at all (e. g. no traffic), <code class="language-plaintext highlighter-rouge">-1</code> is reported to avoid a division by zero. But let’s calculate an average made up of five datapoints:
<code class="language-plaintext highlighter-rouge">((-1) + (-1) + (-1) + (-1) + 100) / 5 = 19.2</code></p>
<p>Due to all the <code class="language-plaintext highlighter-rouge">-1</code> values, the average query cache hit rate gets pulled down to <code class="language-plaintext highlighter-rouge">19.2%</code> - and that perfectly describes my initial problem: the affected app runs only every couple of minutes, the other time it is idle. During idle times, the metric reports <code class="language-plaintext highlighter-rouge">-1</code>, thereby bringing down the average.</p>
<h2 id="conclusion">Conclusion</h2>
<p>There are multiple solutions:</p>
<ol>
<li>Exclude the <code class="language-plaintext highlighter-rouge">-1</code> values in Grafana.</li>
<li>Implement a custom <code class="language-plaintext highlighter-rouge">EventListener</code> and remove the <code class="language-plaintext highlighter-rouge">-1</code> before writing the metric at all.</li>
</ol>
<p>We decided for the second way, but I think it’s really up to the use case.</p>
<p>For me it was a super interesting story to see how EF Core works under the hood, how you can reveal internal behavior by adding more logs and even replace internal components like the <code class="language-plaintext highlighter-rouge">CompiledQueryCache</code> due to .NET’s modular approach - and to take a closer look into the reported metric values 😉</p>
<p>Thanks for reading and have a great time!</p>
Source Browsing in Rider and R#2023-02-27T00:00:00+00:00/2023/02/27/Source-browsing-in-Rider<p>Have you ever spotted something in your codebase that either feels odd or that you don’t understand and want to share it quickly with your colleague? Of course, you could call him/her right away and share your screen, but sometimes this doesn’t feel appropriate, and you’d prefer doing it asynchronously (e. g. via Teams or Slack): <em>Hey buddy, could you please have a look at this particular piece of code?</em></p>
<p>Both Rider and R# (for Visual Studio) allow you creating a URL for the source file so that you can send your colleague e. g. a link to Bitbucket for opening the code in the browser. So let’s see how this can be configured.</p>
<h2 id="configure-source-browsing">Configure Source Browsing</h2>
<p>The relevant feature is called <a href="https://www.jetbrains.com/help/resharper/Options_Source_Browsing.html">Source Browsing</a> and can be used in both R# and Rider. However, R# is necessary for configuration because the corresponding dialog doesn’t yet exist in Rider. Please vote <a href="https://youtrack.jetbrains.com/issue/RIDER-69445">here</a> if you’d like to see it as well ♥</p>
<p>Open the R# settings in Visual Studio and navigate to <em>Environment → Search & Navigation → Source Browsing</em>. Click <em>Add</em> and enter the following values:</p>
<ul>
<li><em>Title</em> → <code class="language-plaintext highlighter-rouge">Bitbucket link to $PATH_PROJECT$:$LINE$ (branch '$GIT_BRANCH_NE$')</code></li>
<li><em>URI pattern</em> → <code class="language-plaintext highlighter-rouge">https://my.bitbucket.instance.com/projects/myProject/repos/myRepo/browse/$GIT_PATH$?at=refs%2Fheads%2F$GIT_BRANCH$#$LINE$</code></li>
</ul>
<p>As you can see in the dialog, there are a couple of macros/placeholders available. The previous template uses:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">$LINE$</code> → the current line within the source file.</li>
<li><code class="language-plaintext highlighter-rouge">$GIT_PATH$</code> → the path of the current file in the Git file system, relative to the Git repo’s root directory.</li>
<li><code class="language-plaintext highlighter-rouge">$PATH_PROJECT$</code> → the path of the current file, relative to the project’s root directory.</li>
<li><code class="language-plaintext highlighter-rouge">$GIT_BRANCH_NE$</code> → the Git branch name, where <code class="language-plaintext highlighter-rouge">NE</code> stands for <em>not escaped</em>, i. e. the value for the Git branch <code class="language-plaintext highlighter-rouge">feature/my-current-feature</code> remains <code class="language-plaintext highlighter-rouge">feature/my-current-feature</code>.</li>
<li><code class="language-plaintext highlighter-rouge">$GIT_BRANCH$</code> → the Git branch name with escaped URL-safe characters, i. e. the value for the Git branch <code class="language-plaintext highlighter-rouge">feature/my-current-feature</code> becomes <code class="language-plaintext highlighter-rouge">feature$2Fmy-current-feature</code>.</li>
</ul>
<p>After saving the template, it becomes active in both Rider and R#:<br />
<img src="/public/post_assets/2023-02-27-Source-browsing-in-Rider/Image_1.png" alt="" /></p>
<p>Executing the action copies the following URL into the clipboard:<br />
<code class="language-plaintext highlighter-rouge">https://my.bitbucket.instance.com/projects/myProject/repos/myRepo/src/BusinessCore/Extensions/JsonConvertExtensions.cs?at=refs%2Fheads%2Fmaster#8</code></p>
<p>Another tiny but very helpful JetBrains feature - learning never stops!</p>
PostgreSQL Arrays and EF Core2023-01-26T00:00:00+00:00/2023/01/26/PostgreSQL-Array-EF-Core<p>We recently faced the challenge in our team of storing a collection of primitive values (in our case <code class="language-plaintext highlighter-rouge">string</code>) within PostgreSQL. I’d like to share some interesting insights and learnings, so let’s go!</p>
<h2 id="imitating-arrays-by-string-concatenation">Imitating arrays by string concatenation</h2>
<p>As we’re using EF Core, our first naive approach was to use a <a href="https://learn.microsoft.com/en-us/ef/core/modeling/backing-field?tabs=data-annotations#field-only-properties">field-only property</a> which looked like this:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="c1">// POCO</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">Book</span>
<span class="p">{</span>
<span class="k">private</span> <span class="kt">string</span> <span class="n">_concatenatedAuthorNames</span><span class="p">;</span>
<span class="k">public</span> <span class="kt">string</span> <span class="n">Key</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
<span class="k">public</span> <span class="kt">string</span><span class="p">[]</span> <span class="nf">GetAuthorNames</span><span class="p">()</span> <span class="p">=></span>
<span class="n">_concatenatedAuthorNames</span><span class="p">.</span><span class="nf">Split</span><span class="p">(</span><span class="s">";"</span><span class="p">,</span> <span class="n">StringSplitOptions</span><span class="p">.</span><span class="n">RemoveEmptyEntries</span><span class="p">);</span>
<span class="k">public</span> <span class="k">void</span> <span class="nf">SetAuthorNames</span><span class="p">(</span><span class="kt">string</span><span class="p">[]</span> <span class="n">authorNames</span><span class="p">)</span> <span class="p">=></span>
<span class="n">_concatenatedAuthorNames</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="nf">Join</span><span class="p">(</span><span class="s">";"</span><span class="p">,</span> <span class="n">authorNames</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// EF Core Configuration</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">BookContext</span> <span class="p">:</span> <span class="n">DbContext</span>
<span class="p">{</span>
<span class="k">public</span> <span class="n">DbSet</span><span class="p"><</span><span class="n">Book</span><span class="p">></span> <span class="n">Books</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
<span class="k">protected</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">OnModelCreating</span><span class="p">(</span><span class="n">ModelBuilder</span> <span class="n">modelBuilder</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">modelBuilder</span><span class="p">.</span><span class="n">Entity</span><span class="p"><</span><span class="n">Book</span><span class="p">>()</span>
<span class="p">.</span><span class="nf">Property</span><span class="p">(</span><span class="s">"_concatenatedAuthorNames"</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p>That worked pretty well, and we built our business logic around this pattern. But to answer questions like <em>which books are written by this particular author</em>, all <code class="language-plaintext highlighter-rouge">Book</code> entities had to be loaded from the database into memory - <em>dark performance problem clouds darken the friendly Swiss Post sky</em>. So this search was moved to the database like this:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="c1">// Business Logic</span>
<span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p"><</span><span class="n">List</span><span class="p"><</span><span class="kt">string</span><span class="p">>></span> <span class="nf">FindAllBooksOfAuthorAsync</span><span class="p">(</span><span class="kt">string</span> <span class="n">authorName</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">await</span> <span class="k">using</span> <span class="nn">BookContext</span> <span class="n">context</span> <span class="p">=</span> <span class="k">await</span> <span class="n">ContextFactory</span><span class="p">.</span><span class="nf">CreateDbContextAsync</span><span class="p">();</span>
<span class="k">return</span> <span class="k">await</span> <span class="n">context</span><span class="p">.</span><span class="n">Books</span>
<span class="p">.</span><span class="nf">Where</span><span class="p">(</span><span class="n">book</span> <span class="p">=></span>
<span class="n">EF</span><span class="p">.</span><span class="n">Functions</span><span class="p">.</span><span class="nf">Like</span><span class="p">(</span><span class="n">EF</span><span class="p">.</span><span class="n">Property</span><span class="p"><</span><span class="kt">string</span><span class="p">>(</span><span class="n">book</span><span class="p">,</span> <span class="s">"_concatenatedAuthorNames"</span><span class="p">),</span> <span class="s">$"%</span><span class="p">{</span><span class="n">authorName</span><span class="p">}</span><span class="s">%"</span><span class="p">))</span>
<span class="p">.</span><span class="nf">ToListAsync</span><span class="p">();</span>
<span class="p">}</span></code></pre></figure>
<p>Which had some downsides as well:</p>
<ul>
<li>A text search with wildcards (<code class="language-plaintext highlighter-rouge">%</code>) is necessary.</li>
<li>The field <code class="language-plaintext highlighter-rouge">_concatenatedAuthorNames</code> must be known but it is <code class="language-plaintext highlighter-rouge">private</code>.</li>
<li>What if there are authors with double names like <em>Shakespeare</em> and <em>Shakespeare-Doyle</em>?</li>
</ul>
<h2 id="using-arrays-on-the-database">Using arrays on the database</h2>
<p>Some days later, a colleague nudged me to give the <a href="https://www.npgsql.org/efcore/mapping/array.html">PostgreSQL Array Type Mapping</a> a try - a feature I was simply unaware of and which lets you store and search arrays on the database-level.
There are basically two ways for using the PostgreSQL data type <code class="language-plaintext highlighter-rouge">text[]</code> in .NET POCOs:</p>
<ol>
<li><code class="language-plaintext highlighter-rouge">string[]</code></li>
<li><code class="language-plaintext highlighter-rouge">List<string></code></li>
</ol>
<p>We strove for using the latter and here’s what our code looked like:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="c1">// POCO</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">Book</span>
<span class="p">{</span>
<span class="k">public</span> <span class="kt">string</span> <span class="n">Key</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
<span class="k">public</span> <span class="n">List</span><span class="p"><</span><span class="kt">string</span><span class="p">></span> <span class="n">AuthorNames</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
<span class="p">}</span>
<span class="c1">// EF Core Configuration</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">BookContext</span> <span class="p">:</span> <span class="n">DbContext</span>
<span class="p">{</span>
<span class="k">public</span> <span class="n">DbSet</span><span class="p"><</span><span class="n">Book</span><span class="p">></span> <span class="n">Books</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
<span class="p">}</span>
<span class="c1">// Business Logic</span>
<span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p"><</span><span class="n">List</span><span class="p"><</span><span class="kt">string</span><span class="p">>></span> <span class="nf">FindAllBooksOfAuthorAsync</span><span class="p">(</span><span class="kt">string</span> <span class="n">authorName</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">await</span> <span class="k">using</span> <span class="nn">BookContext</span> <span class="n">context</span> <span class="p">=</span> <span class="k">await</span> <span class="n">ContextFactory</span><span class="p">.</span><span class="nf">CreateDbContextAsync</span><span class="p">();</span>
<span class="k">return</span> <span class="k">await</span> <span class="n">context</span><span class="p">.</span><span class="n">Books</span>
<span class="p">.</span><span class="nf">Where</span><span class="p">(</span><span class="n">book</span> <span class="p">=></span> <span class="n">book</span><span class="p">.</span><span class="n">AuthorNames</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="n">authorName</span><span class="p">))</span>
<span class="p">.</span><span class="nf">ToListAsync</span><span class="p">();</span>
<span class="p">}</span></code></pre></figure>
<p>Super nice from the .NET perspective because…</p>
<ul>
<li>the business logic doesn’t need to know about any EF Core specific details like <code class="language-plaintext highlighter-rouge">_concatenatedAuthorNames</code>,</li>
<li>the private field <code class="language-plaintext highlighter-rouge">_concatenatedAuthorNames</code> is gone at all,</li>
<li>no need to manually override the model builder in <code class="language-plaintext highlighter-rouge">OnModelCreating()</code>,</li>
<li>well-adopted types like <code class="language-plaintext highlighter-rouge">List<T></code> or LINQ APIs like <code class="language-plaintext highlighter-rouge">.Contains()</code> can be used, feeling very natural.</li>
</ul>
<h2 id="testing-with-sqlite">Testing with SQLite</h2>
<p>All our system integration tests (using a containerized PostgreSQL database) passed after switching the approach - but the unit and integration test suite literally exploded 🤯💣 What happened?</p>
<p>First of all let’s have a look how EF Core translates the <code class="language-plaintext highlighter-rouge">BookContext</code>:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="p">[</span><span class="nf">DbContext</span><span class="p">(</span><span class="k">typeof</span><span class="p">(</span><span class="n">BookContext</span><span class="p">))]</span>
<span class="k">partial</span> <span class="k">class</span> <span class="nc">BookContextModelSnapshot</span> <span class="p">:</span> <span class="n">ModelSnapshot</span>
<span class="p">{</span>
<span class="k">protected</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">BuildModel</span><span class="p">(</span><span class="n">ModelBuilder</span> <span class="n">modelBuilder</span><span class="p">)</span>
<span class="p">{</span>
<span class="cp">#pragma warning disable 612, 618
</span> <span class="n">modelBuilder</span>
<span class="p">.</span><span class="nf">HasAnnotation</span><span class="p">(</span><span class="s">"ProductVersion"</span><span class="p">,</span> <span class="s">"7.0.2"</span><span class="p">)</span>
<span class="p">.</span><span class="nf">HasAnnotation</span><span class="p">(</span><span class="s">"Relational:MaxIdentifierLength"</span><span class="p">,</span> <span class="m">63</span><span class="p">);</span>
<span class="n">NpgsqlModelBuilderExtensions</span><span class="p">.</span><span class="nf">UseIdentityByDefaultColumns</span><span class="p">(</span><span class="n">modelBuilder</span><span class="p">);</span>
<span class="n">modelBuilder</span><span class="p">.</span><span class="nf">Entity</span><span class="p">(</span><span class="s">"Book"</span><span class="p">,</span> <span class="n">b</span> <span class="p">=></span>
<span class="p">{</span>
<span class="n">b</span><span class="p">.</span><span class="n">Property</span><span class="p"><</span><span class="kt">string</span><span class="p">>(</span><span class="s">"Key"</span><span class="p">)</span>
<span class="p">.</span><span class="nf">HasMaxLength</span><span class="p">(</span><span class="m">50</span><span class="p">)</span>
<span class="p">.</span><span class="nf">HasColumnType</span><span class="p">(</span><span class="s">"character varying(50)"</span><span class="p">);</span>
<span class="n">b</span><span class="p">.</span><span class="n">Property</span><span class="p"><</span><span class="n">List</span><span class="p"><</span><span class="kt">string</span><span class="p">>>(</span><span class="s">"AuthorNames"</span><span class="p">)</span>
<span class="p">.</span><span class="nf">IsRequired</span><span class="p">()</span>
<span class="p">.</span><span class="nf">HasColumnType</span><span class="p">(</span><span class="s">"text[]"</span><span class="p">);</span>
<span class="n">b</span><span class="p">.</span><span class="nf">HasKey</span><span class="p">(</span><span class="s">"Key"</span><span class="p">);</span>
<span class="n">b</span><span class="p">.</span><span class="nf">ToTable</span><span class="p">(</span><span class="s">"Book"</span><span class="p">);</span>
<span class="p">});</span>
<span class="cp">#pragma warning restore 612, 618
</span> <span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p>We see that <code class="language-plaintext highlighter-rouge">AuthorNames</code> is mapped to the database type <code class="language-plaintext highlighter-rouge">text[]</code> which exists for PostgreSQL but not for SQLite. Since our unit and integration tests are using an in-memory SQLite database, this has to be adapted.</p>
<p>To not pollute the <code class="language-plaintext highlighter-rouge">BookContext</code> with testing concerns, a dedicated <code class="language-plaintext highlighter-rouge">TestBookContext</code> is used:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">public</span> <span class="k">class</span> <span class="nc">TestBookContext</span> <span class="p">:</span> <span class="n">BookContext</span>
<span class="p">{</span>
<span class="k">public</span> <span class="nf">TestBookContext</span><span class="p">(</span><span class="n">DbContextOptions</span> <span class="n">options</span><span class="p">)</span> <span class="p">:</span> <span class="k">base</span><span class="p">(</span><span class="n">options</span><span class="p">)</span>
<span class="p">{</span>
<span class="p">}</span>
<span class="k">protected</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">OnModelCreating</span><span class="p">(</span><span class="n">ModelBuilder</span> <span class="n">modelBuilder</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">base</span><span class="p">.</span><span class="nf">OnModelCreating</span><span class="p">(</span><span class="n">modelBuilder</span><span class="p">);</span>
<span class="n">ValueComparer</span><span class="p"><</span><span class="n">List</span><span class="p"><</span><span class="kt">string</span><span class="p">>></span> <span class="n">listComparer</span> <span class="p">=</span> <span class="k">new</span><span class="p">(</span>
<span class="p">(</span><span class="n">c1</span><span class="p">,</span> <span class="n">c2</span><span class="p">)</span> <span class="p">=></span> <span class="n">c1</span> <span class="p">!=</span> <span class="k">null</span> <span class="p">&&</span> <span class="n">c2</span> <span class="p">!=</span> <span class="k">null</span> <span class="p">&&</span> <span class="n">c1</span><span class="p">.</span><span class="nf">SequenceEqual</span><span class="p">(</span><span class="n">c2</span><span class="p">),</span>
<span class="n">c</span> <span class="p">=></span> <span class="n">c</span><span class="p">.</span><span class="nf">Aggregate</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span> <span class="p">=></span> <span class="n">HashCode</span><span class="p">.</span><span class="nf">Combine</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">v</span><span class="p">.</span><span class="nf">GetHashCode</span><span class="p">(</span><span class="n">StringComparison</span><span class="p">.</span><span class="n">OrdinalIgnoreCase</span><span class="p">))),</span>
<span class="n">c</span> <span class="p">=></span> <span class="n">c</span><span class="p">.</span><span class="nf">ToList</span><span class="p">());</span>
<span class="n">ValueConverter</span><span class="p"><</span><span class="n">List</span><span class="p"><</span><span class="kt">string</span><span class="p">>,</span> <span class="kt">string</span><span class="p">></span> <span class="n">listConverter</span> <span class="p">=</span> <span class="k">new</span><span class="p">(</span>
<span class="n">strings</span> <span class="p">=></span> <span class="kt">string</span><span class="p">.</span><span class="nf">Join</span><span class="p">(</span><span class="s">","</span><span class="p">,</span> <span class="n">strings</span><span class="p">),</span>
<span class="n">s</span> <span class="p">=></span> <span class="n">s</span><span class="p">.</span><span class="nf">Split</span><span class="p">(</span><span class="s">","</span><span class="p">,</span> <span class="n">StringSplitOptions</span><span class="p">.</span><span class="n">RemoveEmptyEntries</span><span class="p">).</span><span class="nf">ToList</span><span class="p">());</span>
<span class="n">modelBuilder</span>
<span class="p">.</span><span class="n">Entity</span><span class="p"><</span><span class="n">Book</span><span class="p">>()</span>
<span class="p">.</span><span class="nf">Property</span><span class="p">(</span><span class="n">book</span> <span class="p">=></span> <span class="n">book</span><span class="p">.</span><span class="n">AuthorNames</span><span class="p">)</span>
<span class="p">.</span><span class="nf">HasColumnType</span><span class="p">(</span><span class="s">"text"</span><span class="p">)</span>
<span class="p">.</span><span class="nf">HasConversion</span><span class="p">(</span><span class="n">listConverter</span><span class="p">,</span> <span class="n">listComparer</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p>Please notice three important aspects:</p>
<ol>
<li><code class="language-plaintext highlighter-rouge">listComparer</code> → the EF Core Change Tracker will use this comparer to identify changed entities. Without this comparer, EF Core would only detect a completely changed collection (e. g. <code class="language-plaintext highlighter-rouge">book.AuthorNames = new List<string> { "New author" };</code>) but not changes to the collection’s content (e. g. <code class="language-plaintext highlighter-rouge">book.AuthorNames.Add("New author");</code>).</li>
<li><code class="language-plaintext highlighter-rouge">listConverter</code> → as SQLite has no equivalent array datatype, we pick up the string concatenation approach from this post’s beginning and treat the .NET collection (<code class="language-plaintext highlighter-rouge">List<string></code>) as a plain string on the database-level.</li>
<li>Datatype of column <code class="language-plaintext highlighter-rouge">AuthorNames</code> is now <code class="language-plaintext highlighter-rouge">text</code> instead of <code class="language-plaintext highlighter-rouge">text[]</code> and uses the custom <code class="language-plaintext highlighter-rouge">listConverter</code> and <code class="language-plaintext highlighter-rouge">listComparer</code>.</li>
</ol>
<p>Now all unit and integration tests were green 🥳</p>
<h2 id="summary">Summary</h2>
<p>The colleague who nudged me to use this feature found the perfect summary: <em>thank you for going down the rabbit hole with me.</em> I couldn’t agree more! 😅 It was quite a journey to get it all up and running, but in the end I appreciate this solution due to its simplicity and performance.</p>
Fixup commits in Rider2022-11-11T00:00:00+00:00/2022/11/11/Rider-Git-fixup-autosquash<p>Sometimes you’re using tools for years and then, out of a sudden, you discover a long-existing but yet new feature. This is what happened to me this week when I stumbled upon <code class="language-plaintext highlighter-rouge">fixup</code> commits, another wonderful Git feature.</p>
<p>But let’s start from the beginning. I created a sample Git repository with three commits. This is what <code class="language-plaintext highlighter-rouge">git log --oneline</code> outputs:</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell">08c7b38 <span class="o">(</span>HEAD -> main<span class="o">)</span> feat: implement second feature
30c1931 feat: implement first feature
194e8b4 initialize repository</code></pre></figure>
<p>In Rider it looks like this:
<img src="/public/post_assets/2022-11-11-Rider-Git-fixup-autosquash/Image_1.png" alt="" /></p>
<p>E. g. due to code review feedback, some changes need to be made to the commit <code class="language-plaintext highlighter-rouge">30c1931 feat: implement first feature</code> - nothing special, but how do we do this? Quite often I solved this task with a new dedicated commit like <code class="language-plaintext highlighter-rouge">refactor: fix typo</code>. I never liked it because it pollutes the Git history, but well, there are so many things that aren’t perfect, right?</p>
<p>This is where <code class="language-plaintext highlighter-rouge">fixup</code> commits can help 💪🏻 let’s see what Rider offers when right-clicking on the commit in the <em>Git</em> window that we need to fix:
<img src="/public/post_assets/2022-11-11-Rider-Git-fixup-autosquash/Image_2.png" alt="" /></p>
<p>When executing the <em>Fixup…</em> command, we can commit our fixed typo as usual:
<img src="/public/post_assets/2022-11-11-Rider-Git-fixup-autosquash/Image_3.png" alt="" /></p>
<p>Now let’s see how the Git history looks like by executing <code class="language-plaintext highlighter-rouge">git log --oneline</code>:</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell">462e46d <span class="o">(</span>HEAD -> main<span class="o">)</span> fixup! feat: implement first feature
08c7b38 feat: implement second feature
30c1931 feat: implement first feature
194e8b4 initialize repository</code></pre></figure>
<p>Or once again in Rider:
<img src="/public/post_assets/2022-11-11-Rider-Git-fixup-autosquash/Image_4.png" alt="" /></p>
<p>As we can see, a new commit <code class="language-plaintext highlighter-rouge">462e46d fixup! feat: implement first feature</code> has been added, using the commit message from the commit that needs to be fixed with the prefix <code class="language-plaintext highlighter-rouge">fixup! </code>.<br />
This prefix is a Git convention and you can find more about it in the <a href="https://git-scm.com/docs/git-commit#Documentation/git-commit.txt---fixupamendrewordltcommitgt">official Git docs</a>. In combination with another Git feature called <code class="language-plaintext highlighter-rouge">autosquash</code> (<a href="https://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt---autosquash">official Git docs</a>), Git will integrate <code class="language-plaintext highlighter-rouge">fixup</code> commits into the correct commit that needs to be fixed, providing a clean and cohesive history.</p>
<p>On the command-line, you’d do it via <code class="language-plaintext highlighter-rouge">git rebase -i --autosquash 30c1931</code>, where <code class="language-plaintext highlighter-rouge">30c1931</code> is the Git commit hash of the first commit we want to keep.<br />
In Rider all we have to do is right-clicking on this commit and selecting <em>Interactively Rebase from Here…</em>:
<img src="/public/post_assets/2022-11-11-Rider-Git-fixup-autosquash/Image_5.png" alt="" /></p>
<p>Which brings up the following dialog, showing nicely where the <code class="language-plaintext highlighter-rouge">fixup</code> commit will be integrated into:
<img src="/public/post_assets/2022-11-11-Rider-Git-fixup-autosquash/Image_6.png" alt="" /></p>
<p>As we’d expect, <code class="language-plaintext highlighter-rouge">462e46d fixup! feat: implement first feature</code> will be integrated into <code class="language-plaintext highlighter-rouge">30c1931 feat: implement first feature</code>. That’s great! So lets hit <em>Start Rebasing</em> and see how the Git history looks like afterwards:
<img src="/public/post_assets/2022-11-11-Rider-Git-fixup-autosquash/Image_7.png" alt="" /></p>
<p>Or if you prefer on the command-line via <code class="language-plaintext highlighter-rouge">git log --oneline</code>:</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell">08c7b38 <span class="o">(</span>HEAD -> main<span class="o">)</span> feat: implement second feature
30c1931 feat: implement first feature
194e8b4 initialize repository</code></pre></figure>
<p>Now we have a nice and clean Git history without any ugly <code class="language-plaintext highlighter-rouge">fix typo</code> commit - I love it 🤓 I gave it a try in our code review workflow to integrate the reviewer’s feedback as <code class="language-plaintext highlighter-rouge">fixup</code> commits and squash everything at the very last step of the PR.</p>
<p>Thanks for reading and happy rebasing!</p>
Using new C# features in .NET Framework 4.82022-11-09T00:00:00+00:00/2022/11/09/New-CSharp-features-legacy-code<p>When taking a look at the <a href="https://en.wikipedia.org/wiki/C_Sharp_(programming_language)#Versions">version history of C#</a>, the language is picking up pace in the last couple of years: while 13 years passed between C# 1.0 and 6.0, there’s now a new major version coming every year. I personally <em>love</em> the following new features from the latest versions:</p>
<ul>
<li>Nullable reference types</li>
<li>Global <code class="language-plaintext highlighter-rouge">using</code> declarations</li>
<li>File-scoped namespaces</li>
<li>Raw string literals</li>
</ul>
<p>One half of my worktime at Swiss Post I’m working in the parcel sorting. There we’re using the latest .NET stack and all these cool new features. But the other half I’m spending in project which is bound to .NET Framework 4.8 and where I want to use some of the mentioned features as well. Surprisingly this can be done very easily.</p>
<p>The C# compiler offers the property <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-options/language#langversion"><code class="language-plaintext highlighter-rouge">LangVersion</code></a>. The used default language version depends on the used target framework as you can see <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/configure-language-version#defaults">here</a>. And for all versions of the .NET Framework the default C# language version points to <code class="language-plaintext highlighter-rouge">7.3</code>.</p>
<p>Now the trick is pretty easy: open the <code class="language-plaintext highlighter-rouge">csproj</code> of the .NET Framework project and set the <code class="language-plaintext highlighter-rouge">LangVersion</code> property, e. g. to <code class="language-plaintext highlighter-rouge"><LangVersion>11</LangVersion></code> for C# 11 which was releases just recently 💡</p>
<p>This way you can easily use newer C# features on older platforms like .NET Framework 4.8. In my case I created a shared library targeting .NET Standard 2.0 (which uses C# 7.3 by default) with C# 11 features - pretty cool 💪🏻</p>
<p>Thanks for reading!</p>
Why and how I engage on Stack Overflow2022-09-28T00:00:00+00:00/2022/09/28/Stack-Overflow-engagement<p>Some weeks ago I had an interesting talk with one of my colleagues. He told me that he’d appreciate to give some of his knowledge back to the community by participiating actively in the Stack Overflow community. But he also felt a little reluctant due to the rules of this platform, about how to ask good questions, providing good answers and not getting downvoted.<br />
Long story short, he asked me whether I could do an internal presentation about this. Although this hasn’t taken place yet, I decided to share my thoughts here with a greater audience.</p>
<h1 id="my-personal-motivation-for-engaging-in-stack-overflow">My personal motivation for engaging in Stack Overflow</h1>
<p>I created <a href="https://stackoverflow.com/questions/30344927/de-serialize-known-types-similar-to-microsoft">my first question on Stack Overflow</a> on May 20, 2015 and for a very long time, I was merely a consumer of this platform. I asked a question from time to time, but as you can see in my <a href="https://stackoverflow.com/users/4919526/mu88?tab=reputation&sort=graph">reputation graph</a>, around 2019 I got more active by writing comments and answers.</p>
<p>I think it was at that time when I felt mature enough as a software developer to give profound answers. But it was also a certain shift in my personal attitude: to not only “consume” knowledge from the community, but to feed it as well with the experiences I made over the years.<br />
Over time my enthusiasm and conviction about the need for open-source software and its surrounding ecosystems (like the open <em>question and answer</em> platform Stack Overflow) got more traction. This gave me the motivation, but also some kind of obligation to share and help.</p>
<p>Today Stack Overflow is an essential part of my day-to-day business. Of course I’m still reading and learning, but during the day I try to spend some time on commenting and answering to questions I feel confident enough.<br />
But in contrast to my early years, being an <strong>active</strong> member of the software development community (be it Stack Overflow, GitHub, a blog or whatever platform else) has become an integral part. I’d even consider the phrase <em>Sharing is Caring</em> a motivation not just for a job but for the profession of software development.</p>
<h1 id="some-hints-for-your-start-on-stack-overflow">Some hints for your start on Stack Overflow</h1>
<p>I’m pretty sure that there is a myriad of more experienced and suited people to ask what you should and what you shouldn’t do, but here are my key points:</p>
<ol>
<li>Be precise and concise</li>
<li>Focus on your <em>niche of experience</em></li>
<li>Failure is Success</li>
</ol>
<h2 id="be-precise-and-concise">Be precise and concise</h2>
<p>Nobody wants to read lengthy questions or answers unless they contain a lot of value. But too short questions or answers are hard as well, because there is a lot of room for interpretation.That’s why I always ask myself:</p>
<blockquote>
<p>Would I want to read this question/answer? Do I provide enough information about the context / tools / versions?</p>
</blockquote>
<p>If I’m not convinced, I rephrase my text, add additional information, etc. And the best questions for me are still those which include some little repro code that I can paste into my IDE or a sample repo on GitHub.</p>
<h2 id="focus-on-your-niche-of-experience">Focus on your <em>niche of experience</em></h2>
<p>As you might know, Stack Overflow has its own <a href="https://stackoverflow.com/help/tagging">tag concept</a>. In the beginning I was almost unaware of it. But over time I found it a very powerful feature, because it allows me to focus on my <em>niche of experience</em> I feel most comfortable with.</p>
<p>For example, I gathered a lot of knowledge about .NET, C#, the JetBrains product family (Rider, R#, TeamCity) and (unit) testing. But I earned almost no credit for questions related to C# or .NET alone, because there are so many people out there who are more profound and quicker than me.<br />
But especially in the JetBrains space there are not too many active users, giving me an opportunity to share my experiences.</p>
<p>To get rid of all the noise I’m not interested in, I created <a href="https://stackoverflow.com/search?tab=newest&q=(%20[tdd]%20or%20[xunit]%20or%20[nunit]%20or%20[unit-testing]%20or%20[rider]%20or%20[resharper]%20or%20[nuget]%20or%20[msbuild]%20)%20and%20(%20[C%23]%20or%20[rider]%20or%20[resharper]%20or%20[nuget]%20or%20[msbuild]%20)%20and%20(%20answers%3a0%20)">this Stack Overflow query</a>. It only contains the tags I’m interested in, allowing me to focus on my <em>niche of experience</em>.</p>
<h2 id="failure-is-success">Failure is Success</h2>
<p>I’m not a real fan of Stack Overflow’s downvote concept, because I simply don’t believe that humans are good in differentiating between <em>I’m downvoting your question</em> and <em>I’m downvoting you</em>.<br />
But in spite of that I try to see downvotes as a certain form of constructive criticism. Due to my former thoughts, I feel offended when somebody downvotes my question. And yes, there are definitely some unfriendly people and trolls on Stack Overflow. But the majority is not - they are friendly and helpful people.<br />
And this thought helps me to try to accept this, keep calm and try to get behind it:</p>
<blockquote>
<p>Why was it downvoted? Did I miss some information?</p>
</blockquote>
<p>And the next question or answer will be better, because we are humans (most of us 😉): we make mistakes and we learn (also most of us 😉).</p>
<h1 id="closing-thoughts">Closing thoughts</h1>
<p>I hope I could give you some valuable insights and would appreciate your thoughts and motivation about participating on Stack Overflow. Feel free to leave a comment and I hope I’ll see you soon on Stack Overflow 🤓</p>
<p>Thanks for reading and take care!</p>
Enforcing Conventional Commits with a Git hook and Rider2022-07-29T00:00:00+00:00/2022/07/29/Git-hook-conventional-commit<p>In my new team, we’re using <a href="https://www.conventionalcommits.org">Conventional Commits</a>, a specification for Git commit messages. Due to the used conventions it is possible to generate e. g. release notes directly from the Git history.</p>
<p>Unfortunately, I sometimes mess up with the different types of commit or simply forget it, which, as a consequence, breaks the release note generation. That’s why I’ve set up a Git hook which triggers a PowerShell script on every <code class="language-plaintext highlighter-rouge">git commit</code> and checks the commit message.</p>
<p>The PowerShell script looks like this:</p>
<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
</pre></td><td class="code"><pre><span class="c"># The commit message has to be loaded from a Git-internal file</span><span class="w">
</span><span class="nv">$commitMessage</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Get-Content</span><span class="w"> </span><span class="bp">$args</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="w">
</span><span class="c"># Attention: contains a filter for Jira!</span><span class="w">
</span><span class="nv">$conventionalCommitRegex</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"(?-i)^(build|ci|docs|feat|fix|perf|refactor|style|test|chore|revert|BREAKING CHANGE)!?(\([\w\-]+\))?:\s(\w{3}-\d{4})?\s?[a-z]{1}.*"</span><span class="w">
</span><span class="c"># Without Jira filter:</span><span class="w">
</span><span class="c"># $conventionalCommitRegex = "(?-i)^(build|ci|docs|feat|fix|perf|refactor|style|test|chore|revert|BREAKING CHANGE)!?(\([\w\-]+\))?:\s[a-z]{1}.*"</span><span class="w">
</span><span class="nv">$nuKeeperRegex</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"(?-i)^:package: [A-Z].*"</span><span class="w">
</span><span class="kr">if</span><span class="w"> </span><span class="p">(</span><span class="nv">$commitMessage</span><span class="w"> </span><span class="o">-match</span><span class="w"> </span><span class="nv">$conventionalCommitRegex</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="kr">exit</span><span class="w"> </span><span class="mi">0</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="kr">if</span><span class="w"> </span><span class="p">(</span><span class="nv">$commitMessage</span><span class="w"> </span><span class="o">-match</span><span class="w"> </span><span class="nv">$nuKeeperRegex</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="kr">exit</span><span class="w"> </span><span class="mi">0</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="n">Write-Host</span><span class="w"> </span><span class="s2">"The commit message does not match the Conventional Commit rules"</span><span class="w">
</span><span class="kr">exit</span><span class="w"> </span><span class="mi">1</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>Git’s current commit message is stored in the file <code class="language-plaintext highlighter-rouge">.git/COMMIT_EDITMSG</code> which gets read on the first line of the script. Then a pretty long RegEx defines all the different Conventional Commit varieties. This RegEx is executed against the current commit message in line 12. If it matches, <code class="language-plaintext highlighter-rouge">exit 0;</code> indicates success to the Git hook.</p>
<p>To enable this Git hook for a repository, the file <code class="language-plaintext highlighter-rouge">.git/commit-msg</code> has to be created with the following content:</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="c">#!/bin/sh</span>
pwsh <span class="nt">-noprofile</span> C:/source/GitHub/config/hooks/Git_EnsureConventionalCommitMessage.ps1 <span class="nv">$1</span></code></pre></figure>
<p>It is a default Shell script which runs <code class="language-plaintext highlighter-rouge">pwsh</code> (the PowerShell executable) and the formerly explained PowerShell script. The <code class="language-plaintext highlighter-rouge">-noprofile</code> flag skips loading the current user’s PowerShell profile which is unwanted in my case (as I have several things like <a href="https://ohmyposh.dev/">Oh My Posh</a> configured). Finally, the <code class="language-plaintext highlighter-rouge">$1</code> argument is provided by Git and contains the path of the Git commit message file (remember: <code class="language-plaintext highlighter-rouge">.git/COMMIT_EDITMSG</code>).</p>
<p>And that’s all. Now when changing a file and trying to commit it via <code class="language-plaintext highlighter-rouge">git commit -a -m "some arbitrary commit message"</code>, the command fails with <code class="language-plaintext highlighter-rouge">The commit message does not match the Conventional Commit rules</code>.
But when using <code class="language-plaintext highlighter-rouge">git commit -a -m "feat: my fancy new feature"</code>, everything works fine as before.</p>
<p>This is great so far. But the setting has the disadvantage that the Git hook has to be configured for each and every repository. But with Git, there is a solution for everything 👍🏻 Look at my customized <code class="language-plaintext highlighter-rouge">.gitconfig</code>:</p>
<figure class="highlight"><pre><code class="language-conf" data-lang="conf">[<span class="n">includeIf</span> <span class="s2">"gitdir:C:/source/GitHub/"</span>]
<span class="n">path</span> = .<span class="n">gitconfig</span>-<span class="n">github</span></code></pre></figure>
<p>Whenever I’m inside the directory <code class="language-plaintext highlighter-rouge">C:\source\GitHub</code> (where all my cloned GitHub repos live), the additional Git config file <code class="language-plaintext highlighter-rouge">.gitconfig-github</code> with the following additional content gets loaded:</p>
<figure class="highlight"><pre><code class="language-conf" data-lang="conf">[<span class="n">core</span>]
<span class="n">hooksPath</span> = <span class="n">C</span>:/<span class="n">source</span>/<span class="n">GitHub</span>/<span class="n">config</span>/<span class="n">hooks</span></code></pre></figure>
<p>It overrides the path where Git looks for the hooks to execute. So instead of using the default <code class="language-plaintext highlighter-rouge">.git/hooks</code>, this path points to <code class="language-plaintext highlighter-rouge">C:\source\GitHub\config\hooks</code> on my dev machine and inside this folder there is the Git hook <code class="language-plaintext highlighter-rouge">commit-msg</code> and the PowerShell script <code class="language-plaintext highlighter-rouge">Git_EnsureConventionalCommitMessage.ps1</code>.
Now whenever I clone a new repository into <code class="language-plaintext highlighter-rouge">C:\source\GitHub</code>, the Git hook will be applied automatically 🤓</p>
<p>Pretty nice so far! The last piece for me is the Rider plugin <a href="https://plugins.jetbrains.com/plugin/13389-conventional-commit">Conventional Commit</a>. It supports when creating a commit with all the varieties that Conventional Commits offer.</p>
<p>To sum up, the Git hook avoids any invalid commit message after the fact and the Rider plugin supports me in writing correct commit messages before the fact.</p>
<p>Thanks for reading 👋🏻</p>
Debugging a docker-compose file using JetBrains Rider2022-05-18T00:00:00+00:00/2022/05/18/Rider-debug-docker-compose<p>I recently had my first working day in my new team at Swiss Post. As usual there are tons of new stuff to learn, discover and explore. It is super interesting due to its nature in the company’s core domain (parcel management). But it comes with such an amount of new concepts and inputs that it is mind-blowing at the same time 🤯😅</p>
<p>It is my very first time that I’m getting the chance to work with tools like Docker, Kubernetes and Kafka in my everyday professional life. Of course, I’ve seen dozens of talks and read even more blog posts, but putting my own hands on these technologies is a completely different thing and therefore challenging and exciting at the same time.</p>
<p>We have a great sample project which gives new developers an easy way of getting used to these technologies. It consists of a .NET 6 microservice, working in conjunction with a locally hosted Kafka instance and a PostgreSQL database. These different parts are running as Docker containers and getting combined in a <code class="language-plaintext highlighter-rouge">docker-compose.yml</code> file.</p>
<p>When trying to debug this setting with JetBrains Rider, I had several problems, e. g. <a href="https://stackoverflow.com/questions/67406041/debug-docker-with-rider-exited-with-code-244">this</a> and <a href="https://stackoverflow.com/questions/69919664/publish-error-found-multiple-publish-output-files-with-the-same-relative-path">that</a>. With the help of the fabulous JetBrains support, I finally managed it and want to share my findings with you.</p>
<h1 id="set-up-the-run-configuration">Set up the <em>Run Configuration</em></h1>
<p>Make sure that the following options are configured within your <code class="language-plaintext highlighter-rouge">docker-compose</code> <em>Run Configuration</em>:</p>
<ul>
<li>Set the only service to run the application you want to debug (the .NET microservice). All the other services (Kafka, PostgreSQL) will start automatically as the application depends on them.</li>
<li>The containers must be started detached. Therefore, make sure that <em>Attach to: none</em> is set.</li>
</ul>
<p>So it should look like this:</p>
<p><a href="/public/post_assets/2022-05-18-Rider-debug-docker-compose/Image1.png"><img src="/public/post_assets/2022-05-18-Rider-debug-docker-compose/Image1.png" alt="Image" /></a></p>
<p>Save the configuration, set some breakpoints and hit <em>Debug</em>!</p>
<h1 id="troubleshooting">Troubleshooting</h1>
<p>If you’re luckier than me, this will work 😉 but if not, here are some tips that helped me a lot:</p>
<ul>
<li>Stop Rider</li>
<li>Delete all <code class="language-plaintext highlighter-rouge">docker-compose.override*</code> files from <code class="language-plaintext highlighter-rouge">%LOCALAPPDATA%\JetBrains\Rider<<CurrentVersion>>\tmp</code></li>
<li>Try to debug again</li>
</ul>
<p>If these steps do not help, try the following:</p>
<ul>
<li>Delete all data from Docker (<code class="language-plaintext highlighter-rouge">docker system prune -a --volumes</code>)</li>
<li>Clean Rider’s temp folder (<code class="language-plaintext highlighter-rouge">%LOCALAPPDATA%\JetBrains\Rider<<CurrentVersion>>\tmp</code>)</li>
<li>Clean Rider’s cache (<em>File</em> → <em>Invalidate caches</em> → enable <em>Clear file system cache and Local History</em> → <em>Invalidate and Restart</em>)</li>
<li>Recompile everything</li>
<li>Try to debug again</li>
</ul>
<p>Following these steps, I was finally able to debug my <code class="language-plaintext highlighter-rouge">docker-compose.yml</code> file in Rider 🥳 Thanks for reading and take care!</p>
Running ASP.NET Core 6 within Docker on Raspberry Pi 42022-01-08T00:00:00+00:00/2022/01/08/Raspi-DotNet6<p>Yesterday I’ve upgraded all of my personal projects to .NET 6 and I had some issues running them within Docker on my Raspberry Pi 4.</p>
<p>On my Windows 10 dev machine the upgrade worked smoothly. After a couple of minutes, I could run and debug each dockerized application.</p>
<p>As I pulled the new Docker images onto my Raspi running <em>Debian Buster</em> and tried to start the containers as before, nothing happened. Each container exited immediately without any error message. <code class="language-plaintext highlighter-rouge">docker logs <<container ID>></code> returned nothing as well.</p>
<p>After filing a <a href="https://github.com/dotnet/aspnetcore/issues/39372">GitHub issue</a>, <a href="https://github.com/mthalman">mthalman</a> gave me golden hint: the <code class="language-plaintext highlighter-rouge">libseccomp</code> package had to be updated. For this, the following steps were necessary:</p>
<ol>
<li>Add additional source repo for <code class="language-plaintext highlighter-rouge">apt-get</code>:<br /></li>
</ol>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">sudo </span>nano /etc/apt/sources.list
deb http://raspbian.raspberrypi.org/raspbian/ testing main</code></pre></figure>
<ol>
<li>Install the package update:<br /></li>
</ol>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">sudo </span>apt-get <span class="nb">install </span>libseccomp2/testing</code></pre></figure>
<p>After a final reboot I could successfully run my Docker containers based on .NET 6.</p>
<p>Maybe someone of you also has the same problem and this will help you.</p>
<p>Thanks for reading and take care!</p>
One year at Swiss Post 🥳2022-01-01T00:00:00+00:00/2022/01/01/One-Year-Swiss-Post<p>The day after tomorrow I will have my first anniversary at <a href="https://developer.apis.post.ch">Swiss Post</a> 🕺🏻 <br />
Those of you who have been with your employer for a longer period of time might smile: but for me, it was a quite intense time which feels like more than 365 days.</p>
<p>Finding my way into one of Switzerland’s biggest tech employers was an adventure on its own: I had a pretty challenging time at my former employer. With professional help and my wonderful life companion I took the first steps to grow and move forward. I owe them so much credit that I cannot express it in words.</p>
<p>As I was looking for new ways of working I came across Swiss Post’s job offer and decided to apply for a job as senior .NET developer. Already the first interview was very pleasant. The same was true for the second interview.<br />
I think what struck me the most was the openness and flexibility: I had some clear preferences regarding my technology stack, part-time, home office, etc. and for each and every point it was a friendly and productive exchange of ideas - not some sort of cumbersome negotiation. And here I am working at Swiss Post!</p>
<p>And I didn’t regret it. I became part of a nice, humorous and appreciative team. We laugh a lot, there is a close relationship between Dev, Ops and the business specialists. And what is super important for me: the hierarchy is almost flat. Each and every one has the same value for the team. And we all pay it back with good ideas, being open-minded and a warm culture.</p>
<p>Of course, there are conflicts as well. Even after more than six years in Switzerland the different cultural backgrounds come to play every day. And of course we struggle with the intrinsic tech challenges in general.<br />
But due to our team culture, we strive to address problems early and avoid that they get bigger and bigger.</p>
<p>If you ask me what I like the most: well, there are a couple of things 😅 but if I really only have to choose <strong>one</strong> thing, it’s working from home.<br />
I have always felt that in no other industry you can work remotely as good as in tech - and I had a lot of arguing about that with former employers. The last months in the midst of a pandemic proved it: we can deliver high-valuable software in a timely manner and maintain a healthy team spirit.</p>
<p>Coming to an end I perfectly know that the “honeymoon phase” will never last forever. But I can honestly say that - beside the fact that there is always room for improvements 😉 - the working conditions have never been better in my career.</p>
<p>Thanks to all of you who had their share in supporting me through a (working) year that was way better than the three years before. Take care and happy new year!</p>
Migrating from .NET Core 3.1 to .NET 6 - Part 22021-12-01T00:00:00+00:00/2021/12/01/Net6-Migration-Part2<p>In the <a href="/2021/11/30/Net6-Migration-Part1.html">last part</a>, I described some issues during the migration of the EF Core and ASP.NET Core part of our .NET Solution at <a href="https://developer.apis.post.ch">Swiss Post</a>.<br />
This time I will focus on broader things like IDEs and new language/runtime features.</p>
<h1 id="working-with-net-6">Working with .NET 6</h1>
<p>C# 10 and .NET 6 introduced some pretty interesting new features and syntactic sugar. I didn’t introduce all of them to our codebase 😉 but some I find so helpful that I couldn’t resist.</p>
<h2 id="global-using-declarations-and-implicit-usings">Global Using Declarations and Implicit Usings</h2>
<p>We all mentally ignore the first twenty or so lines of each C# file because of all the <code class="language-plaintext highlighter-rouge">using</code> declarations. This is where Global Using Declarations and Implicit Usings can shine. You can learn more about this in <a href="https://devblogs.microsoft.com/dotnet/welcome-to-csharp-10/#global-and-implicit-usings">this great blog post from Microsoft</a>.</p>
<p>I’ve introduced the following file <code class="language-plaintext highlighter-rouge">Directory.Build.props</code> at the root of our repo:</p>
<figure class="highlight"><pre><code class="language-xml" data-lang="xml"><span class="nt"><Project></span>
<span class="nt"><PropertyGroup></span>
<span class="nt"><ImplicitUsings></span>true<span class="nt"></ImplicitUsings></span>
<span class="nt"></PropertyGroup></span>
<span class="nt"><ItemGroup></span>
<span class="nt"><Compile</span> <span class="na">Include=</span><span class="s">"..\GlobalUsings\GlobalUsings.cs"</span><span class="nt">></span>
<span class="nt"><Link></span>GlobalUsings.cs<span class="nt"></Link></span>
<span class="nt"></Compile></span>
<span class="nt"></ItemGroup></span>
<span class="nt"><ItemGroup</span> <span class="na">Condition=</span><span class="s">"$(MSBuildProjectName.EndsWith('.Test'))"</span><span class="nt">></span>
<span class="nt"><Compile</span> <span class="na">Include=</span><span class="s">"..\GlobalUsings\GlobalUsingsForTests.cs"</span><span class="nt">></span>
<span class="nt"><Link></span>GlobalUsingsForTests.cs<span class="nt"></Link></span>
<span class="nt"></Compile></span>
<span class="nt"></ItemGroup></span>
<span class="nt"><ItemGroup</span> <span class="na">Condition=</span><span class="s">"$(MSBuildProjectName.StartsWith('Api.'))"</span><span class="nt">></span>
<span class="nt"><Compile</span> <span class="na">Include=</span><span class="s">"..\GlobalUsings\GlobalUsingsForWebApi.cs"</span><span class="nt">></span>
<span class="nt"><Link></span>GlobalUsingsForWebApi.cs<span class="nt"></Link></span>
<span class="nt"></Compile></span>
<span class="nt"></ItemGroup></span>
<span class="nt"></Project></span></code></pre></figure>
<p>This file gets automatically picked up by the compiler and links the mentioned C# classes into projects matching the naming pattern.</p>
<p>The <code class="language-plaintext highlighter-rouge">GlobalUsings\GlobalUsingsFor*.cs</code> files look like this:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="c1">// GlobalUsings.cs</span>
<span class="k">global</span> <span class="k">using</span> <span class="nn">System.Collections.ObjectModel</span><span class="p">;</span></code></pre></figure>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="c1">// GlobalUsingsForTests.cs</span>
<span class="k">global</span> <span class="k">using</span> <span class="nn">FluentAssertions</span><span class="p">;</span>
<span class="k">global</span> <span class="k">using</span> <span class="nn">Xunit</span><span class="p">;</span>
<span class="k">global</span> <span class="k">using</span> <span class="nn">Xunit.Sdk</span><span class="p">;</span></code></pre></figure>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="c1">// GlobalUsingsForWebApi.cs</span>
<span class="k">global</span> <span class="k">using</span> <span class="nn">Microsoft.AspNetCore.Authorization</span><span class="p">;</span>
<span class="k">global</span> <span class="k">using</span> <span class="nn">Microsoft.AspNetCore.Mvc</span><span class="p">;</span>
<span class="k">global</span> <span class="k">using</span> <span class="nn">System.Net</span><span class="p">;</span>
<span class="k">global</span> <span class="k">using</span> <span class="nn">System.Net.Mime</span><span class="p">;</span></code></pre></figure>
<p>So the next time I create a test assembly named <code class="language-plaintext highlighter-rouge">Yoda.Test</code> (which matches our naming convention), it automatically references xUnit and Fluent Assertions.</p>
<p>Finally I did a Solution-wide cleanup of all <code class="language-plaintext highlighter-rouge">using</code> declarations and could remove so much noise.</p>
<h2 id="file-scoped-namespaces">File-scoped Namespaces</h2>
<p>All the years we C# developers wasted two or four spaces of horizontal indentation although approximately 99% of all C# files contain only a single class. With C# 10, we can rewrite such a file to:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">namespace</span> <span class="nn">MyNamespace</span><span class="p">;</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">MyClass</span>
<span class="p">{</span>
<span class="c1">// more stuff</span>
<span class="p">}</span></code></pre></figure>
<p>Previously it looked like this:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">namespace</span> <span class="nn">MyNamespace</span>
<span class="p">{</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">MyClass</span>
<span class="p">{</span>
<span class="c1">// more stuff</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p><a href="https://www.jetbrains.com/rider/">Rider</a>, my favorite IDE, provides a refactoring to apply this pattern to the whole Solution.</p>
<h2 id="systemtextjson-instead-of-newtonsoftjson"><code class="language-plaintext highlighter-rouge">System.Text.Json</code> instead of <code class="language-plaintext highlighter-rouge">Newtonsoft.Json</code></h2>
<p>Last but not least I worked on a very interesting feature. Over all the years, .NET Framework didn’t have a competitive JSON serializer. <code class="language-plaintext highlighter-rouge">Newtonsoft.Json</code> (aka <em>Json.NET</em>) did an amazing job and was so feature-rich and performant that even Microsoft added it as a reference to previous versions of ASP.NET Core.<br />
But in the last releases, both the <code class="language-plaintext highlighter-rouge">Newtonsoft.Json</code> core developer and Microsoft worked together and created the new JSON serializer <code class="language-plaintext highlighter-rouge">System.Text.Json</code> which is part of .NET’s Base Class Libraries.</p>
<p>You can do pretty crazy stuff with the new serializer (<a href="https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-overview">learn more at Microsoft</a>), like serialization metadata creation at compile-time. But as a first step, I simply tried to use <code class="language-plaintext highlighter-rouge">System.Text.Json</code> as a drop-in replacement for <code class="language-plaintext highlighter-rouge">Newtonsoft.Json</code>.<br />
An in fact that was pretty easy. I’ve just uninstalled all NuGet references, replaced all <code class="language-plaintext highlighter-rouge">JsonConvert.DeserializeObject<T></code> calls with <code class="language-plaintext highlighter-rouge">JsonSerializer.Deserialize<T></code> and it worked in 90% of all cases 💪🏻<br />
The other 10% are spots where the new serializer is more strict than Json.NET. Let me give you some examples.</p>
<h3 id="casing-of-json-properties">Casing of JSON properties</h3>
<p>Imagine the following class to be serialized:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">public</span> <span class="k">class</span> <span class="nc">MyDto</span>
<span class="p">{</span>
<span class="n">Dictionary</span><span class="p"><</span><span class="kt">string</span><span class="p">,</span> <span class="kt">int</span><span class="p">></span> <span class="n">Data</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p>With Json.NET, all keys of <code class="language-plaintext highlighter-rouge">Data</code> were camelcase in the resulting JSON. Now with <code class="language-plaintext highlighter-rouge">System.Text.Json</code>, the keys keep their casing the way there are added to <code class="language-plaintext highlighter-rouge">Data</code>. Take a look at the following example:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="kt">var</span> <span class="n">dto</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Dto</span> <span class="p">{</span> <span class="n">Data</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Dictionary</span><span class="p"><</span><span class="kt">string</span><span class="p">,</span> <span class="kt">string</span><span class="p">></span> <span class="p">{</span> <span class="p">{</span><span class="s">"Bla"</span><span class="p">,</span> <span class="s">"blub"</span><span class="p">}</span> <span class="p">}</span> <span class="p">};</span>
<span class="c1">// Json.NET → {"data": {"bla": "blub"}}</span>
<span class="c1">// System.Text.Json → {"Data": {"Bla": "blub"}}</span></code></pre></figure>
<p>This actually broke some of our client code, but it can be controlled with…</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="kt">var</span> <span class="n">options</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">JsonSerializerOptions</span><span class="p">();</span>
<span class="n">options</span><span class="p">.</span><span class="n">PropertyNamingPolicy</span> <span class="p">=</span> <span class="n">JsonNamingPolicy</span><span class="p">.</span><span class="n">CamelCase</span><span class="p">;</span>
<span class="n">options</span><span class="p">.</span><span class="n">DictionaryKeyPolicy</span> <span class="p">=</span> <span class="n">JsonNamingPolicy</span><span class="p">.</span><span class="n">CamelCase</span><span class="p">;</span></code></pre></figure>
<h3 id="public-property-accessors">Public property accessors</h3>
<p>The following class could be deserialized using Json.NET:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">public</span> <span class="k">class</span> <span class="nc">MyDto</span><span class="p"><</span><span class="n">T</span><span class="p">></span>
<span class="p">{</span>
<span class="k">public</span> <span class="n">List</span><span class="p"><</span><span class="n">T</span><span class="p">></span> <span class="n">Data</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure>
<p>With <code class="language-plaintext highlighter-rouge">System.Text.Json</code>:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">public</span> <span class="k">class</span> <span class="nc">MyDto</span><span class="p"><</span><span class="n">T</span><span class="p">></span>
<span class="p">{</span>
<span class="k">public</span> <span class="n">List</span><span class="p"><</span><span class="n">T</span><span class="p">></span> <span class="n">Data</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<h3 id="public-constructor">Public constructor</h3>
<p>The following class could be deserialized using Json.NET:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">public</span> <span class="k">class</span> <span class="nc">MyDto</span>
<span class="p">{</span>
<span class="k">public</span> <span class="nf">MyDto</span><span class="p">(</span><span class="kt">string</span> <span class="n">name</span><span class="p">)</span> <span class="p">=></span> <span class="n">Name</span> <span class="p">=</span> <span class="n">name</span><span class="p">;</span>
<span class="k">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
<span class="k">public</span> <span class="kt">int</span> <span class="n">Age</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p>With <code class="language-plaintext highlighter-rouge">System.Text.Json</code>:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">public</span> <span class="k">class</span> <span class="nc">MyDto</span>
<span class="p">{</span>
<span class="p">[</span><span class="n">JsonConstructor</span><span class="p">]</span>
<span class="k">public</span> <span class="nf">MyDto</span><span class="p">()</span>
<span class="p">{</span>
<span class="p">}</span>
<span class="k">public</span> <span class="nf">MyDto</span><span class="p">(</span><span class="kt">string</span> <span class="n">name</span><span class="p">)</span> <span class="p">=></span> <span class="n">Name</span> <span class="p">=</span> <span class="n">name</span><span class="p">;</span>
<span class="k">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
<span class="k">public</span> <span class="kt">int</span> <span class="n">Age</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<h1 id="and-what-about-tools-and-ides">And what about tools and IDEs?</h1>
<p>All of our Visual Studio developers had to switch to Visual Studio 2022 since this is the only version supporting .NET 6.</p>
<p>For the JetBrains freaks (including me 🤓) it was a bit of a pain because the JetBrains .NET products (ReSharper and Rider) are not yet ready for .NET 6. They provide preview/EAP versions which are pretty stable and allow us to work with our code base. But due to the preview nature, all of our beloved plugins are not ready yet. Hopefully JetBrains will release the products soon so that all the great plugins out there can follow.</p>
<p>The same is true for other tools we’re using, e. g. TeamCity or Sonar. I had to tweak them a bit because they don’t yet support new language features, e. g. Global Using Declarations.</p>
<h1 id="performance">Performance</h1>
<p>I didn’t do any performance comparisons between .NET Core 3.1 and .NET 6 yet. Maybe I’ll find some time after christmas when it will be a bit more quiet.</p>
<h1 id="closing">Closing</h1>
<p>Especially the second part become rather long, but I hope you appreciated it anyway and you could get some helpful insights!</p>
<p>I enjoyed upgrading our code base as I really love the feeling of having something brought to the latest state. I’m curios which of the cool new features we will adapt in the future.</p>
<p>And I can’t stress enough how important a comprehensive and multi-tiered test suite is. Especially for this upgrade task it was so valuable because I discovered 99% of all issues without even starting the web API or client.</p>
<p>Thanks again for reading, take care and I’d really appreciate your feedback 👋🏻</p>
Migrating from .NET Core 3.1 to .NET 6 - Part 12021-11-30T00:00:00+00:00/2021/11/30/Net6-Migration-Part1<p>Together with my colleagues at <a href="https://developer.apis.post.ch">Swiss Post</a>, I’m working on a .NET Solution with about 20 projects based on .NET Core 3.1. It has a pretty common architecture:</p>
<ul>
<li>Data Access Layer based on Entity Framework Core 3.1</li>
<li>Business Logic based on .NET Core 3.1</li>
<li>Web API based on ASP.NET Core 3.1</li>
</ul>
<p>The front-facing client is written in Angular and for our tests we’re using xUnit and the fabulous <a href="https://github.com/fluentassertions/fluentassertions">Fluent Assertions</a> library. Give them a star if you appreciate their work as much as I do!</p>
<p>I’ve spent the last days migrating our code base to .NET 6 and I want to share some experiences I made and hurdles I’ve cleared.</p>
<h1 id="where-and-how-to-start">Where and how to start?</h1>
<p>.NET 6 allows to reference projects targeting older .NET (Core) version, e. g. .NET Core 3.1, but not the other way round.<br />
Therefore I started the migration from the outside (ASP.NET Core) to the inside (EF Core). That approach ensured that I had a compiling Solution at all times. But I have to admit that the unit and integration tests were not usable during the transition due to runtime assembly conflicts.</p>
<p>The steps for each and every project were pretty much the same:</p>
<ul>
<li>Upgrade the <em>Target Framework Moniker</em> within <code class="language-plaintext highlighter-rouge">*.csproj</code> to <code class="language-plaintext highlighter-rouge">net6.0</code></li>
<li>Upgrade all NuGet packages</li>
<li>Remove unnecessary NuGet packages (always mind the Pathfinder rule, right?)</li>
<li>Fix build warnings related to breaking API changes, obsolete members, etc.</li>
</ul>
<p>Doing this for all of our 20 projects took me about one day. On the one side that feels pretty quick for such a major upgrade. But when considering <strong>which</strong> obstacles I’ve encountered, I’d say I could have been twice as fast 😉</p>
<p>And if you’re asking yourself: <em>did this guy even consider using the <a href="https://dotnet.microsoft.com/platform/upgrade-assistant">.NET Upgrade Assistant</a>?</em> Yes, I did! Before starting with all the manual work, I tried to migrate our Solution with that neat tool.<br />
But I probably used it in the wrong way, because I always ended up with either a partially migrated project (e. g. missing/invalid NuGet packages) or no changes at all (just sticking to .NET Core 3.1).</p>
<h1 id="working-with-aspnet-core-6">Working with ASP.NET Core 6</h1>
<p>Migrating to ASP.NET Core’s new version worked almost fluently. I had some build warnings related to some now nullable properties (e. g. <code class="language-plaintext highlighter-rouge">HttpResponseMessage.Headers.Location</code> or <code class="language-plaintext highlighter-rouge">HttpContext.User.Identity</code>), but fixing that was just a matter of seconds.</p>
<p>There are so many heated discussions whether to use the new <a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis">Minimal APIs</a> or not. I just can say that I didn’t introduce them: not because I don’t like them, but because we have a well working set of controllers which I didn’t want to refactor.</p>
<h1 id="working-with-ef-core-6">Working with EF Core 6</h1>
<p>Upgrading EF Core was more… well, I’d say challenging 😎 compared to ASP.NET Core.</p>
<h2 id="use-entities-instead-of-ids">Use entities instead of IDs</h2>
<p>We had some sort of data seeding on <code class="language-plaintext highlighter-rouge">DbContext</code> creation to provide certain test data to our tests. Unfortunately the data seeder made heavily use of object IDs when referring to other objects. Just consider the following, rather simplified example:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">public</span> <span class="k">class</span> <span class="nc">Author</span>
<span class="p">{</span>
<span class="k">public</span> <span class="kt">int</span> <span class="n">Id</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
<span class="c1">// some stuff</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">Book</span>
<span class="p">{</span>
<span class="k">public</span> <span class="n">Author</span> <span class="n">Author</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
<span class="k">public</span> <span class="kt">int</span> <span class="n">AuthorId</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
<span class="c1">// more stuff</span>
<span class="p">}</span></code></pre></figure>
<p>Within the data seeder, there was code which looked similar to:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="kt">var</span> <span class="n">author</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Author</span> <span class="p">{</span> <span class="n">Id</span> <span class="p">=</span> <span class="m">1</span> <span class="p">};</span>
<span class="kt">var</span> <span class="n">book</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Book</span> <span class="p">{</span> <span class="n">AuthorId</span> <span class="p">=</span> <span class="m">1</span> <span class="p">};</span></code></pre></figure>
<p>This works if the <code class="language-plaintext highlighter-rouge">author</code> entity also gets persisted with an ID of 1. I didn’t analyze it in depth, but that was not longer the case after the upgrade to EF Core 6.<br />
So when running the tests, most of them failed due to some sort of foreign key violation. The fix was pretty simple:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="kt">var</span> <span class="n">author</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">Author</span><span class="p">();</span>
<span class="kt">var</span> <span class="n">book</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Book</span> <span class="p">{</span> <span class="n">Author</span> <span class="p">=</span> <span class="n">author</span> <span class="p">};</span></code></pre></figure>
<p>But finding all the relevant spots took me several hours of boring “fix and try”.</p>
<p>So here comes an advice to my future self: do not rely on object IDs or even manage them on your own when using EF Core! They are an implementation detail and should be considered evil.<br />
Actually I’m following that approach in my private projects by declaring all ID-related properties as <code class="language-plaintext highlighter-rouge">private</code> members.</p>
<h2 id="nullability">Nullability</h2>
<p>Microsoft took the chance and embraced <a href="https://docs.microsoft.com/en-us/dotnet/csharp/nullable-references">Nullable Reference Types</a> (imho one of THE BEST C# features EVER) with EF Core as well.</p>
<p>Therefore some common APIs like <code class="language-plaintext highlighter-rouge">DbContext.FindAsync<T>()</code> are now returning <code class="language-plaintext highlighter-rouge">ValueTask<T?></code>, requiring the caller to do proper null checking. In many cases that was valuable feedback, because it was indeed putting spotlight onto potential null references.</p>
<h2 id="sqlite-for-in-memory-testing">SQLite for in-memory testing</h2>
<p>Some of our test run against an in-memory SQLite database. On test session start, we were calling <code class="language-plaintext highlighter-rouge">DbContext.Database.Migrate()</code>. Our main database in production is SQL Server, so all EF Core Migrations were tailored to that particular DBMS.<br />
But after upgrading, migrating the database within the tests failed with an exception saying that <code class="language-plaintext highlighter-rouge">varchar(max)</code> is not supported. Well, that’s right: <code class="language-plaintext highlighter-rouge">varchar(max)</code> is indeed a SQL Server feature and not supported in SQLite.</p>
<p>Fortunately I came across <a href="https://github.com/dotnet/efcore/issues/7030">this GitHub issue</a> which showed me the trick: calling <code class="language-plaintext highlighter-rouge">DbContext.Database.EnsureCreated()</code> instead of <code class="language-plaintext highlighter-rouge">DbContext.Database.Migrate()</code> helps. Otherwise I’d have to create dedicated EF Core Migrations for SQLite.<br />
But honestly I didn’t check how this did work in the past.</p>
<h1 id="closing">Closing</h1>
<p>That’s it for today! In the <a href="/2021/12/01/Net6-Migration-Part2.html">next part</a>, I will share some broader experiences, e. g. on new language features and IDEs.</p>
<p>Take care and thanks for reading 👋🏻</p>
Leveraging the power of ReSharper Templates2021-03-19T00:00:00+00:00/2021/03/19/ReSharper-Templates<p>Among .NET developers, the <a href="https://www.jetbrains.com/resharper/">JetBrains tool ReSharper (or R#)</a> is some kind of silver bullet when it comes to code analysis and refactoring. However, I’ve often seen developers not being familiar with <a href="https://www.jetbrains.com/resharper/features/code_templates.html">ReSharper Code Templates</a>. That’s why I want to give a brief introduction into this topic.</p>
<p>Basically, Code Templates are little code snippets that can be used in different scopes of coding. JetBrains calls them <em>Live Templates</em>, <em>Surround Templates</em> and <em>File Templates</em>. Let’s take a look at them!</p>
<h2>Live Templates</h2>
<p>Live Templates are snippets that can be inserted while coding in a file. This can be <code class="language-plaintext highlighter-rouge">if</code> statements or <code class="language-plaintext highlighter-rouge">for each</code> loops: just start typing <code class="language-plaintext highlighter-rouge">if</code>, hit <code class="language-plaintext highlighter-rouge">Enter</code> and ReSharper will come up with a little workflow guiding us through the different parts, e. g. the condition.</p>
<p>Out of the box, there are 170+ templates that come with ReSharper. But we can define our own templates via the <em>Templates Explorer</em> (<em>Extensions → ReSharper → Tools → Templates Explorer…</em> in Visual Studio). For example, I have a small template named <code class="language-plaintext highlighter-rouge">xunitasync</code> that looks like this:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="p">[</span><span class="n">Xunit</span><span class="p">.</span><span class="n">Fact</span><span class="p">]</span>
<span class="k">public</span> <span class="k">async</span> <span class="n">Task</span> <span class="err">$</span><span class="n">TestName</span><span class="err">$</span><span class="p">()</span>
<span class="p">{</span>
<span class="c1">// Arrange</span>
<span class="kt">var</span> <span class="n">testee</span> <span class="p">=</span> <span class="k">new</span> <span class="err">$</span><span class="n">TestType</span><span class="err">$</span><span class="p">();</span>
<span class="err">$</span><span class="n">END</span><span class="err">$</span>
<span class="c1">// Act</span>
<span class="c1">// Assert</span>
<span class="k">throw</span> <span class="k">new</span> <span class="n">System</span><span class="p">.</span><span class="nf">NotImplementedException</span><span class="p">();</span>
<span class="p">}</span></code></pre></figure>
<p>When typing <code class="language-plaintext highlighter-rouge">xunitasync</code> within a class, this template will create an asynchronous xUnit test body for me. When looking closer to the template code, we notice the two strings <code class="language-plaintext highlighter-rouge">$TestName$</code> and <code class="language-plaintext highlighter-rouge">$END$</code>. These are Template Parameters and they are context-aware. In the most easiest way, they are just plain strings that ReSharper will ask us to enter when using the template. For example, the <code class="language-plaintext highlighter-rouge">$TestName$</code> is just the name of the test we want to use. <code class="language-plaintext highlighter-rouge">$END$</code> is the location where the caret will be placed after the template has been applied. In my case, I want to start writing the <em>Arrange</em> part of the test.</p>
<p>By <em>context-aware</em>, I mean that such parameters can be more than just plain strings. For example, a parameter can be configured so that whenever it is applied, IntelliSense will pop-up and a type is requested. This happens in the example for the parameter <code class="language-plaintext highlighter-rouge">$TestType$</code>. JetBrains calls them <em>Template Macros</em> and there’s a bunch of them (<a href="https://www.jetbrains.com/help/resharper/Template_Macros.html">see here</a>).</p>
<p>I have plenty of those live templates, e. g. for creating To-do items or Get-Only C# Properties.</p>
<h2>Surround Templates</h2>
<p>The second interesting option are Surround Templates. They can be used to surround a selected piece of code with another piece of code. Let’s look at the following template:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="kt">var</span> <span class="n">stopwatch</span> <span class="p">=</span> <span class="n">System</span><span class="p">.</span><span class="n">Diagnostics</span><span class="p">.</span><span class="n">Stopwatch</span><span class="p">.</span><span class="nf">StartNew</span><span class="p">();</span>
<span class="err">$</span><span class="n">SELECTION</span><span class="err">$</span>
<span class="n">stopwatch</span><span class="p">.</span><span class="nf">Stop</span><span class="p">();</span><span class="err">$</span><span class="n">END</span><span class="err">$</span></code></pre></figure>
<p>We are already familiar with the <code class="language-plaintext highlighter-rouge">$END$</code> parameter. The <code class="language-plaintext highlighter-rouge">$SELECTION$</code> parameter represents the piece of code that is selected. Before this code, a <a href="https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.stopwatch.startnew?view=net-5.0"><code class="language-plaintext highlighter-rouge">Stopwatch</code></a> instance <code class="language-plaintext highlighter-rouge">stopwatch</code> is created and started. After the selected code, <code class="language-plaintext highlighter-rouge">stopwatch</code> is stopped. I use this frequently as the most trivial form of performance measurement.</p>
<p>With this template, the following code…</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="kt">var</span> <span class="n">service</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">MyCustomService</span><span class="p">();</span>
<span class="k">await</span> <span class="n">service</span><span class="p">.</span><span class="nf">ExecuteLongRunningProcessAsync</span><span class="p">();</span></code></pre></figure>
<p>…becomes to…</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="kt">var</span> <span class="n">stopwatch</span> <span class="p">=</span> <span class="n">System</span><span class="p">.</span><span class="n">Diagnostics</span><span class="p">.</span><span class="n">Stopwatch</span><span class="p">.</span><span class="nf">StartNew</span><span class="p">();</span>
<span class="kt">var</span> <span class="n">service</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">MyCustomService</span><span class="p">();</span>
<span class="k">await</span> <span class="n">service</span><span class="p">.</span><span class="nf">ExecuteLongRunningProcessAsync</span><span class="p">();</span>
<span class="n">stopwatch</span><span class="p">.</span><span class="nf">Stop</span><span class="p">();</span></code></pre></figure>
<h2>File Templates</h2>
<p>I tend to use them not as much as Live or Surround Templates, but File Templates are also very handy in some situations. For example, when working with MSTest, each test class has to be decorated with the <code class="language-plaintext highlighter-rouge">[TestClass]</code> attribute. I have a File Template that creates a MSTest file for me and it looks like this:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">using</span> <span class="nn">Microsoft.VisualStudio.TestTools.UnitTesting</span><span class="p">;</span>
<span class="k">namespace</span> <span class="err">$</span><span class="nn">Namespace</span><span class="err">$</span>
<span class="p">{</span>
<span class="p">[</span><span class="n">TestClass</span><span class="p">]</span>
<span class="k">public</span> <span class="k">class</span> <span class="err">$</span><span class="nc">TestClassName</span><span class="err">$</span>
<span class="p">{</span>
<span class="err">$</span><span class="n">END</span><span class="err">$</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p>Again, there are some parameters which are pretty obvious: <code class="language-plaintext highlighter-rouge">$Namespace$</code> stands for the Namespace and it will be automatically retrieved by a macro. So I never have to take care of this manually, it will be determined by the file location within the Solution.</p>
<h2>Postfix Templates</h2>
<p>It is not an exaggeration to say that I like these the most. With Postfix Templates, I can write <code class="language-plaintext highlighter-rouge">new MyService().var</code> and the little suffix <code class="language-plaintext highlighter-rouge">.var</code> will create a variable. Or we can use <code class="language-plaintext highlighter-rouge">.foreach</code> to create a <code class="language-plaintext highlighter-rouge">for each</code> loop.</p>
<p>Unfortunately, we cannot create Postfix Templates through the <em>Templates Explorer</em> yet. But I hope the JetBrains guys will make this possible in the future 😃.</p>
<h2>Summary</h2>
<p>This was a short introduction into the concept of ReSharper Templates. There is a ton more to say, but I hope this is enough to catch your curiosity. For me in personal, I can say that ReSharper templates have revolutionized the way that I code. I can keep my focus on <strong>what</strong> I want to write, not <strong>how</strong>. And the extensibility gives me a way to be more productive in small, but repetitious coding tasks.</p>
<p>If you need more information, I’d recommend the excellent <a href="https://www.jetbrains.com/resharper/features/code_templates.html">documentation</a>. There is a list of all the available macros, GIFs to see the Templates in action and much more.</p>
<p>Last but not least, I’d be curious whether you’ve created a custom template and for what.</p>
<p>Thank you for reading and take care!</p>
Utilizing Docker when testing performance enhancements2021-02-12T00:00:00+00:00/2021/02/12/Docker-Testing<p>With the beginning of 2021, I started my new job as a Full Stack Developer for <a href="https://www.post.ch/en/">Swiss Post</a>. My first task was to improve the performance of a long-running ASP.NET Core Web API endpoint. As usual, I was writing unit and integration tests to ensure that the new code is doing what it is supposed to.</p>
<p>When finished with coding, I wanted to measure how the enhancements apply to a larger, more realistic dataset. I decided to figure out how I can make use of Docker containers.</p>
<p>For local development, we’re using <a href="https://docs.microsoft.com/de-de/sql/database-engine/configure-windows/sql-server-express-localdb">SQL Server LocalDB</a>. Since Microsoft shipped WSL 2, Linux Docker Containers are super fast on Windows 10 and I switched over to <a href="https://hub.docker.com/_/microsoft-mssql-server">SQL Server for Linux</a>. I created a container with the following command:</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell">docker run <span class="nt">--name</span> sqlserver2017 <span class="nt">-e</span> <span class="s1">'ACCEPT_EULA=Y'</span> <span class="nt">-e</span> <span class="s1">'SA_PASSWORD=yourStrong(!)Password'</span> <span class="nt">-p</span> 1433:1433 <span class="nt">-d</span> mcr.microsoft.com/mssql/server:2017-latest</code></pre></figure>
<p>Now I applied our database schema using an Entity Framework Core Migration. This resulted in a complete but empty database. I created a snapshot of my current data state so that I could always come back to my starting point. I did so by calling:</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell">docker commit sqlserver2017 mu88/db:initialized</code></pre></figure>
<p>This command created a new Docker Image <code class="language-plaintext highlighter-rouge">mu88/db:initialized</code> by taking a snapshot from the container <code class="language-plaintext highlighter-rouge">sqlserver2017</code>.</p>
<p>Thankfully, my wise colleagues had already created some Web API endpoints to seed test data. So all I had to do was a little <code class="language-plaintext highlighter-rouge">HTTP POST</code> and wait a couple of minutes to generate my test data (lets say for 50 customers). After that I was ready to test my new code. Since the mentioned long-running process does some data mutation, I created another snapshot:</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell">docker commit sqlserver2017 mu88/db:test_50_customers</code></pre></figure>
<p>Now I could trigger the long-running process and extracted some relevant metrics after it was finished. The numbers were not so clear, that’s why I decided to do another test with 500 customers.</p>
<p>For this, I had to stop and remove the actually running container and go back to my starting point <code class="language-plaintext highlighter-rouge">mu88/db:initialized</code>:</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell">docker stop sqlserver2017
docker <span class="nb">rm </span>sqlserver2017
docker run <span class="nt">--name</span> sqlserver2017 <span class="nt">-e</span> <span class="s1">'ACCEPT_EULA=Y'</span> <span class="nt">-e</span> <span class="s1">'SA_PASSWORD=yourStrong(!)Password'</span> <span class="nt">-p</span> 1433:1433 <span class="nt">-d</span> mu88/db:initialized</code></pre></figure>
<p>Please note the very last line: I started a new container using my snapshot image. Now I did another <code class="language-plaintext highlighter-rouge">HTTP POST</code> to generate 500 customers and created another snapshot:</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell">docker commit sqlserver2017 mu88/db:test_500_customers</code></pre></figure>
<p>After another performance test, the metrics looked good, but we immediately found another code spot that yelled for optimization. For the testing, I could rely on my snapshots by calling</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell">docker stop sqlserver2017
docker <span class="nb">rm </span>sqlserver2017
docker run <span class="nt">--name</span> sqlserver2017 <span class="nt">-e</span> <span class="s1">'ACCEPT_EULA=Y'</span> <span class="nt">-e</span> <span class="s1">'SA_PASSWORD=yourStrong(!)Password'</span> <span class="nt">-p</span> 1433:1433 <span class="nt">-d</span> mu88/db:test_50_customers</code></pre></figure>
<p>or</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell">docker stop sqlserver2017
docker <span class="nb">rm </span>sqlserver2017
docker run <span class="nt">--name</span> sqlserver2017 <span class="nt">-e</span> <span class="s1">'ACCEPT_EULA=Y'</span> <span class="nt">-e</span> <span class="s1">'SA_PASSWORD=yourStrong(!)Password'</span> <span class="nt">-p</span> 1433:1433 <span class="nt">-d</span> mu88/db:test_500_customers</code></pre></figure>
<p>With every round of “code & measure”, that approach became more and more valuable because I could rely on a set of data snapshots.</p>
<p>For me this was a super interesting lesson of how container technologies can help when it comes to testing.</p>
<p>I hope this was interesting for you. Thanks for reading!</p>
Is .NET Core cool enough to cool a Raspberry Pi? - Part 22020-04-24T00:00:00+00:00/2020/04/24/Raspi-Fan-Controller_p2<p>In the <a href="/2020/04/24/Raspi-Fan-Controller_p1">last post</a>, I mainly described how to set up the software for the <a href="https://github.com/mu88/RaspiFanController">Raspberry Pi Fan Controller</a>. In this part, I will focus on the hardware part and bringing everything together.</p>
<h2 id="bring-it-all-together">Bring it all together</h2>
<p>During the development, I could easily test my app by using the <em>Inverse of Control pattern</em> and utilizing Dependency Injection to inject a fake temperature provider and fan controller. At a certain point, I was ready to test it on the Raspi.</p>
<p>At first, I was convinced to deploy the app via Docker. But after some time, I was not sure whether a <code class="language-plaintext highlighter-rouge">sudo</code> command executed from within a Docker container will be forwarded to the OS (remember the temperature measurement). So I decided to ship it as a <a href="https://docs.microsoft.com/en-us/dotnet/core/deploying/#publish-self-contained">self-contained executable</a>. This can be done as follows:</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell">dotnet publish <span class="nt">-r</span> linux-arm <span class="nt">-c</span> Release /p:PublishSingleFile<span class="o">=</span><span class="nb">true</span></code></pre></figure>
<p>The following command copies the build results to the Raspi:</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell">scp <span class="nt">-r</span> <span class="se">\b</span><span class="k">in</span><span class="se">\R</span>elease<span class="se">\n</span>etcoreapp3.1<span class="se">\l</span>inux-arm<span class="se">\p</span>ublish pi@raspberry:/tmp/RaspiFanController/</code></pre></figure>
<p>On the Raspi, we have to allow the app to be executed:</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">chmod </span>777 /tmp/RaspiFanController/RaspiFanController</code></pre></figure>
<p>And finally, start the app using <code class="language-plaintext highlighter-rouge">sudo</code>. This is important because otherwise, reading the temperature doesn’t work.</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">sudo</span> /tmp/RaspiFanController/RaspiFanController</code></pre></figure>
<p>There were some firewall/reverse proxy issues in my case, but that would be beyond this post. In the end, I could successfully access the app via http://raspberry:5000/cool and it was showing the current temperature.</p>
<h2 id="soldering">Soldering</h2>
<p>This was definitely the hardest part for me in this project. But several other blog posts like the following gave me the necessary information which components I had to buy and how to connect them:</p>
<ul>
<li><a href="https://howchoo.com/g/ote2mjkzzta/control-raspberry-pi-fan-temperature-python">Automatically Control Your Raspberry Pi Fan (and Temperature) with Python</a></li>
<li><a href="https://fizzy.cc/raspberry-pi-fan/">Temperature Controlled Fan for Raspberry Pi 4</a></li>
</ul>
<p>Finally, I bought:</p>
<ul>
<li>Breadboard</li>
<li>Red LED</li>
<li>Transistor BC 337</li>
<li>Resistor 680 Ω for the Transistor</li>
<li>Resistor 1 kΩ for the LED</li>
<li>Jumper wires</li>
</ul>
<p>Because I was afraid to somehow destroy the fan, I made a first test with my controller software and the LED:</p>
<p><img src="/public/post_assets/2020-04-24-Raspi-Fan-Controller/Image2.jpg" alt="" /></p>
<p>After a successful test, I switched over and used the fan:</p>
<p><img src="/public/post_assets/2020-04-24-Raspi-Fan-Controller/Image3.jpg" alt="" /></p>
<p>And it was working! So I had no more excuses to solder everything and do the final assembly:</p>
<p><img src="/public/post_assets/2020-04-24-Raspi-Fan-Controller/Image4.jpg" alt="" /></p>
<h2 id="register-the-app-as-a-service">Register the app as a service</h2>
<p>Now that everything was working fine, I wanted to register my little app as a service. This will ensure that the controller automatically gets started after a reboot.</p>
<p>For this, I had to create a <em>service unit configuration file</em> on the Raspi:</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">sudo </span>nano /etc/systemd/system/RaspiFanController.service</code></pre></figure>
<p>It has the following content:</p>
<figure class="highlight"><pre><code class="language-config" data-lang="config">[<span class="n">Unit</span>]
<span class="n">Description</span>=<span class="n">Raspberry</span> <span class="n">Pi</span> <span class="n">Fan</span> <span class="n">Controller</span> <span class="n">based</span> <span class="n">on</span> <span class="n">Blazor</span> <span class="n">Server</span>
[<span class="n">Service</span>]
<span class="n">WorkingDirectory</span>=/<span class="n">tmp</span>/<span class="n">RaspiFanController</span>
<span class="n">ExecStart</span>=/<span class="n">tmp</span>/<span class="n">RaspiFanController</span>/<span class="n">RaspiFanController</span> &
<span class="n">Restart</span>=<span class="n">always</span>
<span class="c"># Restart service after 10 seconds if the dotnet service crashes:
</span><span class="n">RestartSec</span>=<span class="m">10</span>
<span class="n">SyslogIdentifier</span>=<span class="n">raspifancontroller</span>
<span class="n">User</span>=<span class="n">root</span>
<span class="n">Environment</span>=<span class="n">ASPNETCORE_ENVIRONMENT</span>=<span class="n">Production</span>
[<span class="n">Install</span>]
<span class="n">WantedBy</span>=<span class="n">multi</span>-<span class="n">user</span>.<span class="n">target</span></code></pre></figure>
<p>With the following commands, the service will be created:</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">sudo cp </span>RaspiFanController.service /etc/systemd/system/RaspiFanController.service
<span class="nb">sudo </span>systemctl daemon-reload
<span class="nb">sudo </span>systemctl start RaspiFanController</code></pre></figure>
<p>Now the app will start on every reboot.</p>
<h2 id="summary">Summary</h2>
<p>Because of recent developments in the .NET ecosystem, I was able to write a controller for a Linux device like a Raspberry Pi. I could leverage all the new features my favorite platform provides:</p>
<ul>
<li>Cross-platform</li>
<li>Worker Services</li>
<li>ASP.NET Core Blazor Server</li>
</ul>
<p>In theory, this app could be easily ported to any other OS like Windows 10 IoT Core or device like Arduino - if the necessary parts like temperature retrieval are available.</p>
<p>For me, this was another great experience of how the modern development world can look like: serve every platform with the tools of your choice.</p>
<p>Thank you for reading!</p>
Is .NET Core cool enough to cool a Raspberry Pi? - Part 12020-04-24T00:00:00+00:00/2020/04/24/Raspi-Fan-Controller_p1<p>A couple of weeks ago, I bought a new toy: a <a href="https://www.raspberrypi.org/products/raspberry-pi-4-model-b/">Raspberry Pi 4 Model B</a>. I wanted to set up my own DNS server, but mainly, I wanted to get in contact with this new platform. Since I had already read other blog posts saying that the Raspi gets quite warm under normal conditions, I ordered a case with a built-in fan as well.<br />
The shipment arrived and I assembled everything curiously. First impression: wow, the ramp-up time to assemble and install is super fast! Second impression: the built-in fan is a bit loud… and my spouse thought so as well :wink: So the Raspi had to live its first days in the kitchen.</p>
<p>In the next days, I found several blog posts describing how to build a small electric circuit and a bit of software to control the fan. The hardware part was new to me anyway. But for the software, the blog authors were mostly using Python. Since my heart beats for .NET and C#, I was intrigued by the idea of using my favorite technologies. And I found the <a href="https://github.com/dotnet/iot">.NET Core IoT Libraries</a> - a NuGet package provided by Microsoft to build applications for devices like the Raspi. This package was my missing piece in the puzzle - how to control the hardware. Now I was on fire and decided to build a fan controller based on Blazor Server and the found NuGet package.</p>
<p>All the code can be found in my GitHub repo <a href="https://github.com/mu88/RaspiFanController">Raspi Fan Controller</a>. Lets focus on the main parts:</p>
<ul>
<li>Temperature provider</li>
<li>Temperature controller</li>
<li>Fan controller</li>
<li>Frontend</li>
</ul>
<h2 id="temperature-provider">Temperature provider</h2>
<p>To control the temperature, we need to measure it, right? Fortunately, the Raspi’s OS <em>Raspian</em> comes with a built-in command to retrieve its current temperature:</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">sudo </span>vcgencmd measure_temp</code></pre></figure>
<p>It returns a text like <code class="language-plaintext highlighter-rouge">temp=39.0°C</code>. The class <a href="https://github.com/mu88/RaspiFanController/blob/master/RaspiFanController/Logic/RaspiTemperatureProvider.cs"><code class="language-plaintext highlighter-rouge">Logic\RaspiTemperatureProvider</code></a> does a little bit of RegEx to parse the current temperature and unit into a tuple <code class="language-plaintext highlighter-rouge">(39.0, "C")</code>.</p>
<p>After retrieving the current temperature, we can act on it.</p>
<h2 id="temperature-controller">Temperature controller</h2>
<p>The temperature controller <a href="https://github.com/mu88/RaspiFanController/blob/master/RaspiFanController/Logic/RaspiTemperatureController.cs"><code class="language-plaintext highlighter-rouge">Logic\RaspiTemperatureController</code></a> is nothing but a <code class="language-plaintext highlighter-rouge">while</code> loop. It regularly checks the current temperature and turns on the fan if a upper threshold is reached and turns it off if a lower threshold is reached.<br />
This loop is <code class="language-plaintext highlighter-rouge">async</code>: since the temperature controller will be started by a <a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-3.1&tabs=visual-studio">.NET Core Worker Service</a>, the <code class="language-plaintext highlighter-rouge">while</code>’s exit condition is the <code class="language-plaintext highlighter-rouge">CancellationToken</code> provided by the ASP.NET Core environment.<br />
In between, there is a sleep time between two loop runs via <code class="language-plaintext highlighter-rouge">Task.Delay()</code>.</p>
<h2 id="fan-controller">Fan controller</h2>
<p>This is the place where we really access the hardware. The Raspi has so called <em>General Purpose Input/Output</em> (GPIO) pins - physical pins on its board that can be used for custom extension. The NuGet package <a href="https://github.com/dotnet/iot">.NET Core IoT Library</a> abstracts and allows us to set these pins in a very easy way. Take a look into <a href="https://github.com/mu88/RaspiFanController/blob/master/RaspiFanController/Logic/RaspiFanController.cs"><code class="language-plaintext highlighter-rouge">Logic\RaspiFanController</code></a>:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="kt">var</span> <span class="n">gpioController</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">GpioController</span><span class="p">();</span>
<span class="n">gpioController</span><span class="p">.</span><span class="nf">OpenPin</span><span class="p">(</span><span class="m">17</span><span class="p">,</span> <span class="n">PinMode</span><span class="p">.</span><span class="n">Output</span><span class="p">);</span>
<span class="n">gpioController</span><span class="p">.</span><span class="nf">Write</span><span class="p">(</span><span class="m">17</span><span class="p">,</span> <span class="n">PinValue</span><span class="p">.</span><span class="n">High</span><span class="p">);</span></code></pre></figure>
<p>The turn on the fan, the GPIO pin 17 is set to high value. And that’s it.</p>
<h2 id="frontend">Frontend</h2>
<p>The user interface is a single Razor page providing the necessary information like current temperature and threshold. These information are read from the temperature controller.</p>
<p><img src="/public/post_assets/2020-04-24-Raspi-Fan-Controller/Image1.jpg" width="350" /></p>
<p>In the <a href="/2020/04/24/Raspi-Fan-Controller_p2">next part</a>, I will describe how to bring everything together.</p>
Dependency injection and legacy code2020-02-07T00:00:00+00:00/2020/02/07/Refactor-Dependency-Injection<p>During the last couple of months, I was doing a major refactoring of the dependency injection infrastructure on the product I build with my colleagues. The application relies heavily on the <a href="https://en.wikipedia.org/wiki/Service_locator_pattern">service locator pattern</a>. To improve the testability, a refactoring pattern evolved that some other people might find useful.</p>
<p>Let’s start with an example showing the initial situation:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
</pre></td><td class="code"><pre><span class="k">public</span> <span class="k">class</span> <span class="nc">CarFactory</span>
<span class="p">{</span>
<span class="k">public</span> <span class="n">ICar</span> <span class="nf">ConstructCar</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">return</span> <span class="k">new</span> <span class="nf">Car</span><span class="p">(</span><span class="n">ServiceLocator</span><span class="p">.</span><span class="n">Resolve</span><span class="p"><</span><span class="n">IEngine</span><span class="p">>(),</span>
<span class="n">ServiceLocator</span><span class="p">.</span><span class="n">Resolve</span><span class="p"><</span><span class="n">IChassis</span><span class="p">>());</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">static</span> <span class="k">class</span> <span class="nc">ServiceLocator</span>
<span class="p">{</span>
<span class="k">public</span> <span class="k">static</span> <span class="n">T</span> <span class="n">Resolve</span><span class="p"><</span><span class="n">T</span><span class="p">>()</span>
<span class="p">{</span>
<span class="c1">// Resolve instance from registered service registrations</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>The component to refactor is <code class="language-plaintext highlighter-rouge">CarFactory</code>. As you can see, a global static <code class="language-plaintext highlighter-rouge">ServiceLocator</code> is used to obtain the engine and chassis instances building up the car to construct. Writing a unit test for this class can be cumbersome because you have to consider the global service locator. Furthermore, the <code class="language-plaintext highlighter-rouge">ServiceLocator</code> obscures the usage of further dependencies like <code class="language-plaintext highlighter-rouge">IEngine</code> and <code class="language-plaintext highlighter-rouge">IChassis</code>.</p>
<p>The pure idea of dependency injection would teach us to refactor the code to something like this:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
</pre></td><td class="code"><pre><span class="k">public</span> <span class="k">class</span> <span class="nc">CarFactory</span>
<span class="p">{</span>
<span class="k">public</span> <span class="nf">CarFactory</span><span class="p">(</span><span class="n">IEngine</span> <span class="n">engine</span><span class="p">,</span> <span class="n">IChassis</span> <span class="n">chassis</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">Engine</span> <span class="p">=</span> <span class="n">engine</span><span class="p">;</span>
<span class="n">Chassis</span> <span class="p">=</span> <span class="n">chassis</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">public</span> <span class="n">IEngine</span> <span class="n">Engine</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">private</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="c1">// public for testing</span>
<span class="k">public</span> <span class="n">IChassis</span> <span class="n">Chassis</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">private</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="c1">// public for testing</span>
<span class="k">public</span> <span class="n">ICar</span> <span class="nf">ConstructCar</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">return</span> <span class="k">new</span> <span class="nf">Car</span><span class="p">(</span><span class="n">Engine</span><span class="p">,</span> <span class="n">Chassis</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>Now we’re requesting the necessary dependencies via constructor injection. For unit testing, this is a perfect situation, because now we can inject mocks that mimic the required behavior and everything works fine.</p>
<p>But since we’re not using the service locator anymore, somebody has to provide the necessary dependencies within the production code.<br />
Sure, we could use a composition root and a dependency injection container. But depending on the circumstances (size of application, amount of time, etc.), this can become a very hard piece of work or even almost impossible.<br />
Instead of using constructor injection, we could set up an integration test with a differently configured service locator. But whenever possible, I tend to favour unit over integration tests because they are usually faster and have a narrower scope.</p>
<p>So basically, there are two seemingly competing demands:</p>
<ul>
<li>Don’t change the public API in order to keep the production code as untouched as possible.</li>
<li>Increase the testability.</li>
</ul>
<p>And this is how I tended to consolidate the two demands:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
</pre></td><td class="code"><pre><span class="k">public</span> <span class="k">class</span> <span class="nc">CarFactory</span>
<span class="p">{</span>
<span class="k">public</span> <span class="nf">CarFactory</span><span class="p">()</span>
<span class="p">:</span> <span class="k">this</span><span class="p">(</span><span class="n">ServiceLocator</span><span class="p">.</span><span class="n">Resolve</span><span class="p"><</span><span class="n">IEngine</span><span class="p">>(),</span>
<span class="n">ServiceLocator</span><span class="p">.</span><span class="n">Resolve</span><span class="p"><</span><span class="n">IChassis</span><span class="p">>())</span>
<span class="p">{</span>
<span class="p">}</span>
<span class="k">private</span> <span class="nf">CarFactory</span><span class="p">(</span><span class="n">IEngine</span> <span class="n">engine</span><span class="p">,</span> <span class="n">IChassis</span> <span class="n">chassis</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">Engine</span> <span class="p">=</span> <span class="n">engine</span><span class="p">;</span>
<span class="n">Chassis</span> <span class="p">=</span> <span class="n">chassis</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">public</span> <span class="n">IEngine</span> <span class="n">Engine</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">private</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="c1">// public for testing and a very very very very very long comment</span>
<span class="k">public</span> <span class="n">IChassis</span> <span class="n">Chassis</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">private</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="c1">// public for testing</span>
<span class="k">public</span> <span class="n">ICar</span> <span class="nf">ConstructCar</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">return</span> <span class="k">new</span> <span class="nf">Car</span><span class="p">(</span><span class="n">Engine</span><span class="p">,</span> <span class="n">Chassis</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>As you can see, the approach is pretty close to the former one using constructor injection. The difference lies in the two constructors: we still have the constructor specifying all the necessary dependencies, but it is declared <code class="language-plaintext highlighter-rouge">private</code>.<br />
The <code class="language-plaintext highlighter-rouge">public</code> constructor still defines no parameters. However, it is calling the private constructor and resolves the necessary dependencies using the <code class="language-plaintext highlighter-rouge">ServiceLocator</code>. This way, nothing changes in terms of the component’s public API and behavior.</p>
<p>But then what is the added value in terms of unit testing? Unlike the C# compiler, .NET allows the use of private constructors via reflection (<a href="https://docs.microsoft.com/en-us/dotnet/api/system.activator.createinstance?view=netframework-4.8#System_Activator_CreateInstance_System_Type_System_Boolean_">see here</a>). This enables us to call the private constructor from an unit test.<br />
Doing so manually for each and every unit test would be a pain. Fortunately, there are packages like <a href="https://github.com/moq/Moq.AutoMocker">AutoMocker for Moq</a> that take away the pain. Using that package, our test looks like this:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
</pre></td><td class="code"><pre><span class="p">[</span><span class="n">TestClass</span><span class="p">]</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">CarFactoryTests</span>
<span class="p">{</span>
<span class="p">[</span><span class="n">TestMethod</span><span class="p">]</span>
<span class="k">public</span> <span class="k">void</span> <span class="nf">ConstructCar</span><span class="p">()</span>
<span class="p">{</span>
<span class="c1">// Arrange</span>
<span class="kt">var</span> <span class="n">autoMocker</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">AutoMocker</span><span class="p">();</span>
<span class="n">autoMocker</span><span class="p">.</span><span class="n">Setup</span><span class="p"><</span><span class="n">IEngine</span><span class="p">,</span><span class="kt">string</span><span class="p">>(</span><span class="n">x</span><span class="p">=></span><span class="n">x</span><span class="p">.</span><span class="n">Type</span><span class="p">).</span><span class="nf">Returns</span><span class="p">(</span><span class="s">"V6"</span><span class="p">);</span>
<span class="kt">var</span> <span class="n">testee</span> <span class="p">=</span> <span class="n">autoMocker</span><span class="p">.</span><span class="n">CreateInstance</span><span class="p"><</span><span class="n">CarFactory</span><span class="p">>(</span><span class="k">true</span><span class="p">);</span>
<span class="c1">// Act</span>
<span class="kt">var</span> <span class="n">result</span> <span class="p">=</span> <span class="n">testee</span><span class="p">.</span><span class="nf">ConstructCar</span><span class="p">();</span>
<span class="c1">// Assert</span>
<span class="n">result</span><span class="p">.</span><span class="nf">Should</span><span class="p">().</span><span class="nf">NotBeNull</span><span class="p">();</span>
<span class="n">result</span><span class="p">.</span><span class="n">Engine</span><span class="p">.</span><span class="n">Type</span><span class="p">.</span><span class="nf">Should</span><span class="p">().</span><span class="nf">Be</span><span class="p">(</span><span class="s">"V6"</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>Using this refactoring technique enabled me to write unit tests for a whole bunch components of our application.</p>
<p>But it is important to keep one thing in mind: a private constructor is not marked as <code class="language-plaintext highlighter-rouge">private</code> just for fun. There are reasons why the component’s creator chose it that way. Furthermore, we’re bypassing the compiler via reflection - usually not the best idea :wink:<br />
So this technique is more like medicine: use it only in small doses or preferably not at all. Whenever possible, go for dependency injection all the way.</p>
<p>Happy coding and thank you for reading!</p>
Localizing texts in a server-side Blazor app2020-01-24T00:00:00+00:00/2020/01/24/Blazor-Translation<p>Like so many of us, I’m playing around with the different varieties of Blazor. Recently, I’ve ported an application based on Angular 2 and Electron to Blazor Server and <a href="https://github.com/ElectronNET">Electron.NET</a>. Thereby, I came across the topic of translating the app.</p>
<p>Technically, my purpose was not just translating the app into a specific language. I wanted to do it properly. Maybe you’ve stumbled across the terms <code class="language-plaintext highlighter-rouge">i18n</code> and <code class="language-plaintext highlighter-rouge">l10n</code>. Those cryptic acronyms stand for <em>internationalization</em> and <em>localization</em>. The first one simply means: <em>I modify my code in a way so that it is capable of handling different languages.</em> The second one means: <em>Now lets introduce the translations for the different languages.</em> So <code class="language-plaintext highlighter-rouge">i18n</code> can be considered as the base of <code class="language-plaintext highlighter-rouge">l10n</code> and it allows you to add new languages without changing the code consuming it.<br />
Do you wonder where these strange names come from? It is pretty simple: take VS Code or Notepad++, paste the words <code class="language-plaintext highlighter-rouge">internationalization</code> and <code class="language-plaintext highlighter-rouge">localization</code> into it and count the number of characters between <code class="language-plaintext highlighter-rouge">i</code> and <code class="language-plaintext highlighter-rouge">n</code> resp. <code class="language-plaintext highlighter-rouge">l</code> and <code class="language-plaintext highlighter-rouge">n</code>: <code class="language-plaintext highlighter-rouge">18</code> and <code class="language-plaintext highlighter-rouge">10</code>.</p>
<p>But how to apply <code class="language-plaintext highlighter-rouge">i18n</code> and <code class="language-plaintext highlighter-rouge">l10n</code> to Blazor Server? At the time of porting my app (a couple of month ago), there were almost no information about that topic. So I had to find my own solution.</p>
<p>Since Blazor Server is an ASP.NET Core application, I’ve written an injectable component called <code class="language-plaintext highlighter-rouge">CustomTranslator</code>:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">public</span> <span class="k">class</span> <span class="nc">CustomTranslator</span> <span class="p">:</span> <span class="n">ICustomTranslator</span>
<span class="p">{</span>
<span class="k">public</span> <span class="nf">CustomTranslator</span><span class="p">(</span><span class="n">IStringLocalizer</span><span class="p"><</span><span class="n">CustomTranslator</span><span class="p">></span> <span class="n">localizer</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">Localizer</span> <span class="p">=</span> <span class="n">localizer</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">public</span> <span class="kt">string</span> <span class="nf">GetTranslation</span><span class="p">(</span><span class="kt">string</span> <span class="n">text</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">Localizer</span><span class="p">[</span><span class="n">text</span><span class="p">];</span>
<span class="p">}</span>
<span class="k">private</span> <span class="n">IStringLocalizer</span><span class="p"><</span><span class="n">CustomTranslator</span><span class="p">></span> <span class="n">Localizer</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="p">}</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">interface</span> <span class="nc">ICustomTranslator</span>
<span class="p">{</span>
<span class="kt">string</span> <span class="nf">GetTranslation</span><span class="p">(</span><span class="kt">string</span> <span class="n">text</span><span class="p">);</span>
<span class="p">}</span></code></pre></figure>
<p>As you can see, the interface <code class="language-plaintext highlighter-rouge">ICustomTranslator</code> accepts a string and returns the translation. The implementation <code class="language-plaintext highlighter-rouge">CustomTranslator</code> does a lookup in a property called <code class="language-plaintext highlighter-rouge">Localizer</code>. This component is a built-in functionality of ASP.NET Core (<a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization?view=aspnetcore-3.1">see here</a>) and it comes from the Dependency Injection container.</p>
<p>To use the custom translation service, it has to be registered within <code class="language-plaintext highlighter-rouge">Startup.cs</code>:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">public</span> <span class="k">class</span> <span class="nc">Startup</span>
<span class="p">{</span>
<span class="k">public</span> <span class="k">void</span> <span class="nf">ConfigureServices</span><span class="p">(</span><span class="n">IServiceCollection</span> <span class="n">services</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">services</span><span class="p">.</span><span class="nf">AddLocalization</span><span class="p">(</span><span class="n">options</span> <span class="p">=></span> <span class="n">options</span><span class="p">.</span><span class="n">ResourcesPath</span> <span class="p">=</span> <span class="s">"Resources"</span><span class="p">);</span>
<span class="n">services</span><span class="p">.</span><span class="n">AddSingleton</span><span class="p"><</span><span class="n">ICustomTranslator</span><span class="p">,</span> <span class="n">CustomTranslator</span><span class="p">>();</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">void</span> <span class="nf">Configure</span><span class="p">(</span><span class="n">IApplicationBuilder</span> <span class="n">app</span><span class="p">,</span> <span class="n">IWebHostEnvironment</span> <span class="n">env</span><span class="p">)</span>
<span class="p">{</span>
<span class="kt">var</span> <span class="n">supportedCultures</span> <span class="p">=</span> <span class="k">new</span><span class="p">[]</span>
<span class="p">{</span>
<span class="k">new</span> <span class="nf">CultureInfo</span><span class="p">(</span><span class="s">"en"</span><span class="p">),</span>
<span class="k">new</span> <span class="nf">CultureInfo</span><span class="p">(</span><span class="s">"de"</span><span class="p">),</span>
<span class="p">};</span>
<span class="n">app</span><span class="p">.</span><span class="nf">UseRequestLocalization</span><span class="p">(</span><span class="k">new</span> <span class="n">RequestLocalizationOptions</span>
<span class="p">{</span>
<span class="n">DefaultRequestCulture</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">RequestCulture</span><span class="p">(</span><span class="s">"de"</span><span class="p">),</span>
<span class="n">SupportedCultures</span> <span class="p">=</span> <span class="n">supportedCultures</span><span class="p">,</span>
<span class="n">SupportedUICultures</span> <span class="p">=</span> <span class="n">supportedCultures</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p>Let’s go through this step by step. Within <code class="language-plaintext highlighter-rouge">ConfigureServices()</code>, basic localization is enabled and we’re telling ASP.NET Core to look for all translations in the resource folder <code class="language-plaintext highlighter-rouge">Resources</code>. Next, the custom translation component is registered within the Dependency Injection container as a singleton.<br />
The array <code class="language-plaintext highlighter-rouge">supportedCultures</code> within <code class="language-plaintext highlighter-rouge">Configure()</code> defines all the languages the application will support. The following line configures the application to support the requested languages and defines that the default language of my application is German (<code class="language-plaintext highlighter-rouge">de</code>).</p>
<p>Now we can go to any Razor page and the Dependency Injection container will provide the custom translation component:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="n">@inject</span> <span class="n">ICustomTranslator</span> <span class="n">Translator</span></code></pre></figure>
<p>By using the following code, the translator can be consumed:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="p"><</span><span class="n">p</span><span class="p">></span><span class="n">@Translator</span><span class="p">.</span><span class="nf">GetTranslation</span><span class="p">(</span><span class="s">"SomeString"</span><span class="p">)</</span><span class="n">p</span><span class="p">></span></code></pre></figure>
<p>Of course, we could also consume the translation component in any other class provided by the Dependency Injection container:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">public</span> <span class="k">class</span> <span class="nc">MyBusinessLogicService</span>
<span class="p">{</span>
<span class="k">public</span> <span class="nf">MyBusinessLogicService</span><span class="p">(</span><span class="n">ICustomTranslator</span> <span class="n">translator</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">CustomTranslator</span> <span class="p">=</span> <span class="n">translator</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">private</span> <span class="n">ICustomTranslator</span> <span class="n">CustomTranslator</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="p">}</span>
<span class="k">private</span> <span class="k">void</span> <span class="nf">MyMethod</span><span class="p">()</span>
<span class="p">{</span>
<span class="kt">var</span> <span class="n">translation</span> <span class="p">=</span> <span class="n">CustomTranslator</span><span class="p">.</span><span class="nf">GetTranslation</span><span class="p">(</span><span class="s">"SomeString"</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p>And that’s all! Well, almost :wink: The code would work, but it wouldn’t do anything because there are no translations yet. By creating the two resource files <code class="language-plaintext highlighter-rouge">Resources\CustomTranslator.en.resx</code> (English) and <code class="language-plaintext highlighter-rouge">Resources\CustomTranslator.de.resx</code> (German) and adding the key <code class="language-plaintext highlighter-rouge">SomeString</code> with an appropriate translation, the mission is completed. When running the app, the UI will be localized in German.</p>
<p>If I’d like to add support for French, all I had to do would be:</p>
<ul>
<li>Create a file <code class="language-plaintext highlighter-rouge">Resources\CustomTranslator.fr.resx</code> containing all the translations.</li>
<li>Add <code class="language-plaintext highlighter-rouge">new CultureInfo("fr")</code> to the array <code class="language-plaintext highlighter-rouge">supportedCultures</code> in <code class="language-plaintext highlighter-rouge">Startup.Configure()</code>.</li>
</ul>
<p>As you’ve hopefully seen, doing <code class="language-plaintext highlighter-rouge">i18n</code> and <code class="language-plaintext highlighter-rouge">l10n</code> with Blazor Server is not difficult at all. For people being familiar with ASP.NET Core, it should be super straight-forward.<br />
For me, the biggest challenge was to understand that the folder name <code class="language-plaintext highlighter-rouge">Resources</code> and file names <code class="language-plaintext highlighter-rouge">CustomTranslator.<<language>>.resx</code> have to match an exact pattern. Otherwise, they won’t be recognized.</p>
<p>Thank you for reading!</p>
Profiling a .NET Core 3.0 Console App running in Docker for Windows with dotTrace2019-12-07T00:00:00+00:00/2019/12/07/Docker-dotTrace<p>Recently, I was asked to profile a .NET Console App running in Docker for Windows. I’m a big fan of the JetBrains tools for .NET: ReSharper, dotPeek, dotTrace - they are all part of my toolbelt. Since I’ve never profiled a Docker container with dotTrace, this post shall illustrate how to do this.</p>
<p>First of all, we need some code to profile. The complete code can be downloaded from <a href="https://github.com/mu88/DockerDotTrace">this GitHub repo</a>. But basically, it is nothing more than this:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Threading.Tasks</span><span class="p">;</span>
<span class="k">namespace</span> <span class="nn">TestWithDocker</span>
<span class="p">{</span>
<span class="k">internal</span> <span class="k">class</span> <span class="nc">Program</span>
<span class="p">{</span>
<span class="k">private</span> <span class="k">static</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">Main</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">while</span> <span class="p">(</span><span class="k">true</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">await</span> <span class="n">Task</span><span class="p">.</span><span class="nf">Delay</span><span class="p">(</span><span class="m">1000</span><span class="p">);</span>
<span class="nf">DoSomeWork</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">private</span> <span class="k">static</span> <span class="k">void</span> <span class="nf">DoSomeWork</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">var</span> <span class="n">i</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="p"><</span> <span class="m">100</span><span class="p">;</span> <span class="n">i</span><span class="p">++)</span>
<span class="p">{</span>
<span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="k">new</span> <span class="nf">Random</span><span class="p">().</span><span class="nf">Next</span><span class="p">());</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p>As we can see, every second 100 random numbers will be generated and printed to the console.</p>
<p>Furthermore, a <code class="language-plaintext highlighter-rouge">Dockerfile</code> is needed and it looks like this:</p>
<figure class="highlight"><pre><code class="language-docker" data-lang="docker"><span class="k">FROM</span><span class="s"> mcr.microsoft.com/windows/servercore:1903</span>
<span class="k">COPY</span><span class="s"> bin/Release/netcoreapp3.0/win-x64/* ./</span>
<span class="k">ENTRYPOINT</span><span class="s"> ["TestWithDocker.exe"]</span></code></pre></figure>
<p>We’re pulling the base image <code class="language-plaintext highlighter-rouge">mcr.microsoft.com/windows/servercore:1903</code>, adding the compiled application and setting it as the <code class="language-plaintext highlighter-rouge">ENTRYPOINT</code>.</p>
<p>Before building the Docker image, the application has to be built using the <code class="language-plaintext highlighter-rouge">dotnet</code> global tool:</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell">dotnet publish <span class="nt">-c</span> Release</code></pre></figure>
<p>Afterwards, we can build the Docker image:</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell">docker build <span class="nt">-t</span> test-with-docker .</code></pre></figure>
<p>Before running and profiling the container, please make sure that you have dotTrace installed and if not happened yet, make your self comfortable with the how-to <a href="https://www.jetbrains.com/help/profiler/Starting_Remote_Profiling_Session.html">Starting Remote Profiling Session</a>. In summary, it says the following:</p>
<ul>
<li>Unzip <code class="language-plaintext highlighter-rouge">RemoteAgent.zip</code> to the environment to profile (in our case the Docker container).</li>
<li>Start dotTrace and connect to the <strong>Remote Agent URL</strong>. By default, the Remote Agent uses port 9100.</li>
<li>Attach to the application.</li>
</ul>
<p>So lets do this step by step. At first, we will start the Docker container and map the container port 9100 to its local pendant:</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell">docker run <span class="nt">-d</span> <span class="nt">-p</span> 9100:9100 <span class="nt">--name</span> <span class="nb">test </span>test-with-docker</code></pre></figure>
<p>To copy the unzipped Remote Agent, the following command has to be executed:</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell">docker <span class="nb">cp </span>RemoteAgent/. <span class="nb">test</span>:/RemoteAgent</code></pre></figure>
<p>This copies all the content from the host’s folder <code class="language-plaintext highlighter-rouge">RemoteAgent</code> to the container’s folder <code class="language-plaintext highlighter-rouge">RemoteAgent</code>. In case this commands fails saying that you cannot copy content while a container is running: this seems to be a Windows/Hyper V limitation. We can work around this by stopping the container, copying the content and finally starting it again:</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell">docker stop <span class="nb">test
</span>docker <span class="nb">cp </span>RemoteAgent/. <span class="nb">test</span>:/RemoteAgent
docker start <span class="nb">test</span></code></pre></figure>
<p>Now the Remote Agent is there, but is has to be started:</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell">docker <span class="nb">exec</span> <span class="nt">-d</span> <span class="nb">test </span>RemoteAgent/RemoteAgent.exe</code></pre></figure>
<p>Finally, we can connect to our application using dotTrace. As <strong>Remote Agent URL</strong>, we use <code class="language-plaintext highlighter-rouge">net.tcp://localhost:9100/RemoteAgent</code>. This accesses the local port 9100 of my machine which is mapped to port 9100 of the Docker container where the Remote Agent is up and running. Now we can attach dotTrace to <code class="language-plaintext highlighter-rouge">TestWithDocker.exe</code> and collect snapshots as usual.</p>
<p><img src="/public/post_assets/2019-12-07-Docker-dotTrace/image1.jpg" alt="" /></p>
<p>As you can see in the following screenshot, everything works as usual when profiling an application and we find our method <code class="language-plaintext highlighter-rouge">DoSomeWork()</code>:</p>
<p><img src="/public/post_assets/2019-12-07-Docker-dotTrace/image2.jpg" alt="" /></p>
Dive into your Android's device network traffic2019-06-17T00:00:00+00:00/2019/06/17/Android-Fiddler<p>Recently, I came across the challenge to analyze the network traffic of my Android smartphone. I wanted to know which HTTP requests a specific app executes if the user triggers an UI action. When analyzing any app on a Windows PC, my silver bullet is <a href="https://www.telerik.com/fiddler">Fiddler</a>. So I was curious how to do this with my smartphone.</p>
<p>The general approach is to make Fiddler the smartphone’s network proxy. For this, both PC and smartphone have to be in the same network.</p>
<p>At first, we need an installation of Fiddler. This web debugging proxy has tons of options, but comes with a well-defined out-of-the-box setting. After starting the application, we immediately see the incoming and outgoing traffic from the browser or mail client.</p>
<p><img src="/public/post_assets/2019-06-17-Android-Fiddler/fiddler_1.jpg" alt="" /></p>
<p>This works because Fiddler registers itself as a local proxy running on port <code class="language-plaintext highlighter-rouge">8888</code>. The port can be changed in the options: <em>Tools -> Options -> Connections -> Fiddler listens on port</em></p>
<p><img src="/public/post_assets/2019-06-17-Android-Fiddler/fiddler_2.jpg" alt="" /></p>
<p>While we’re here, we enable the option <em>Allow remote computers to connect</em>. This will allow the Android phone to use Fiddler as its proxy.</p>
<p><img src="/public/post_assets/2019-06-17-Android-Fiddler/fiddler_3.jpg" alt="" /></p>
<p>That’s it about configuring Fiddler. Finally, we can set up the Android device to use the proxy. To do so, open the settings of the WiFi to use and expand the <em>Advanced options</em>.</p>
<p><img src="/public/post_assets/2019-06-17-Android-Fiddler/wifi_settings_1.jpg" alt="" /></p>
<p>Now we have to enter the following values:</p>
<ul>
<li><em>Proxy</em> = <code class="language-plaintext highlighter-rouge">Manual</code></li>
<li><em>Proxy hostname</em> = IP address of your PC where Fiddler is running (e. g. <code class="language-plaintext highlighter-rouge">192.168.178.53</code>)</li>
<li><em>Proxy port</em> = Port on your PC on which Fiddler is listening (e.g. <code class="language-plaintext highlighter-rouge">8888</code>)</li>
</ul>
<p><img src="/public/post_assets/2019-06-17-Android-Fiddler/wifi_settings_2.jpg" alt="" /></p>
<p>To see whether it works we can simply check this by trying to open an arbitrary website in the browser. Now we should see all the network traffic within Fiddler. If doesn’t work, check your firewall whether incoming traffic to Fiddler’s port is allowed.</p>
Build and debug Qooxdoo with Visual Studio Code2019-05-25T00:00:00+00:00/2019/05/25/Qooxdoo-VS-Code<p>Because of my job, I got in touch with the JavaScript framework <a href="https://www.qooxdoo.org/">Qooxdoo</a>. I’ve never heard about it before, since the web development solar system seems to rotate around Angular, React and all the other big frameworks.</p>
<p>The process of building is pretty straightforward: you simply have to run a Python script, usually called <code class="language-plaintext highlighter-rouge">generate.py</code>. That does all the ceremony of bundling, etc. and gives you the final application.</p>
<p>When it comes to debugging of the application, you can put on your usual “web developer tool belt”: open the HTML in your favorite browser and use it’s <em>Developer Tools</em>.</p>
<p>For me as a Visual Studio loving C# developer, it is still odd to have three different components to build and debug my app:</p>
<ul>
<li>IDE to develop</li>
<li>Console to build</li>
<li>Browser tools to debug</li>
</ul>
<p>Of course, these are primarily prejudices. I have read too many articles not to know that it is much easier today. So I wanted to combine all the three steps into on environment, which is Visual Studio Code.</p>
<p>I’ll pass the first step, since it is not worth mentioning the I can write JavaScript code in VS Code :grin: Let’s focus on the second step, which is building the application. According to the <a href="https://code.visualstudio.com/docs/editor/tasks">Microsoft documentation</a>, I’ve set up a <code class="language-plaintext highlighter-rouge">.vscode\tasks.json</code> with the following content:</p>
<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="p">{</span><span class="w">
</span><span class="nl">"version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2.0.0"</span><span class="p">,</span><span class="w">
</span><span class="nl">"tasks"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Build HelloWorld"</span><span class="p">,</span><span class="w">
</span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"shell"</span><span class="p">,</span><span class="w">
</span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"${workspaceFolder}/generate.py"</span><span class="p">,</span><span class="w">
</span><span class="nl">"group"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"kind"</span><span class="p">:</span><span class="w"> </span><span class="s2">"build"</span><span class="p">,</span><span class="w">
</span><span class="nl">"isDefault"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">]</span><span class="w">
</span><span class="p">}</span></code></pre></figure>
<p>It creates a default build task called <em>Build HelloWorld</em> (which is my sample application) that simply calls the Python generator script.</p>
<p>Lastly, there is the step of debugging my built application right from VS Code. Again, the <a href="https://code.visualstudio.com/Docs/editor/debugging">Microsoft documentation</a> was very helpful. To debug an application, I had to set up the file <code class="language-plaintext highlighter-rouge">.vscode\launch.json</code> in the following way:</p>
<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="p">{</span><span class="w">
</span><span class="nl">"version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"0.2.0"</span><span class="p">,</span><span class="w">
</span><span class="nl">"configurations"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"chrome"</span><span class="p">,</span><span class="w">
</span><span class="nl">"request"</span><span class="p">:</span><span class="w"> </span><span class="s2">"launch"</span><span class="p">,</span><span class="w">
</span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Launch HelloWorld"</span><span class="p">,</span><span class="w">
</span><span class="nl">"file"</span><span class="p">:</span><span class="w"> </span><span class="s2">"${workspaceFolder}/source/index.html"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">]</span><span class="w">
</span><span class="p">}</span></code></pre></figure>
<p>This creates the launch profile <em>Launch HelloWorld</em> which launches Google Chrome with my application. Furthermore, the IDE gets attached to the browser and I can set breakpoints in VS Code.</p>
<p>For me, this is a pretty convenient setting which reduces a C# developers anxiety to work with JavaScript code :wink: If you want a jump start, you can use my sample application which is available on <a href="https://github.com/mu88/QooxdooHelloWorld">GitHub</a>.</p>
Map Service Search2018-04-19T00:00:00+00:00/2018/04/19/Map-Service-Search<p>Almost a year ago, I gave a talk at the <a href="https://fmeuc.com/">FME User Conference in Vancouver</a> with the title <em>“Hey dude, where is my Workspace?”</em> This talk was focussed on the question how to keep track of all the FME Workspaces flying around all over the enterprise. In my case, this challenge was tremendously simplified by a tool which allowed to search through the metadata of Workspaces hosted on several instances of <a href="https://www.safe.com/fme/fme-server/">FME Server</a>.<br />
Since that time, I was curious whether it would be possible to adapt the approach and apply it to our <a href="https://www.esri.com/en-us/arcgis/products/arcgis-enterprise/overview">ArcGIS Server</a> farm. With the growing demand for <em>Map Services</em> (HTTP services serving map content) and therefore growing infrastructure, it gets harder to stay on top of things and answer questions like:</p>
<ul>
<li>Which Map Services use a specific dataset?</li>
<li>Which ArcGIS Server instance hosts the Map Service named <em>XYZ</em>?</li>
<li>etc.</li>
</ul>
<h2 id="the-idea-gets-born">The idea gets born</h2>
<p>For quite some time I know that you can see all the <a href="http://enterprise.arcgis.com/en/server/latest/publish-services/windows/reviewing-service-workspaces-in-manager.htm">used workspaces of a Map Service in <em>ArcGIS Server Manager</em></a>. But until a couple of weeks ago, I’ve never asked myself the question: <strong>WHERE ARE THEY COMING FROM?</strong><br />
With the help of Chrome’s Developer Tools, I discovered that <em>ArcGIS Server Manager</em> uses the <a href="https://developers.arcgis.com/rest/services-reference/rest-api-admin.htm">ArcGIS Server Administrator API</a> for this, more precisely it calls <code class="language-plaintext highlighter-rouge">https://<<Name of ArcGIS Server>>/arcgis/admin/services/<<Name of Map Service>>.MapServer/iteminfo/manifest/manifest.json</code>. This JSON object contains all the desired information.</p>
<h2 id="from-theory-to-practice">From theory to practice</h2>
<p>Based on this discovery, I’ve built a web crawler which determines all Map Services of an ArcGIS Server, retrieves their corresponding <code class="language-plaintext highlighter-rouge">manifest.json</code> and extracts the desired information. I’m a big fan of the FME platform, so my hammer for this nail was a FME Workspace. Since it is all about a couple of HTTP requests, the Workspace is quite simple. By doing this, the process provides the following information:</p>
<ul>
<li>Name of the Map Service</li>
<li>Environment of the ArcGIS Server (e. g. <em>Staging</em> or <em>Production</em>)</li>
<li>Type of the ArcGIS Server (e. g. <em>Intranet</em> or <em>Internet</em>)</li>
<li>All used datasources with the following details:
<ul>
<li>Type of the datasource (e. g. <em>SDE</em> or <em>File Geodatabase</em>)</li>
<li>Name of the datasource (e. g. name of the Oracle or SQL Server instance)</li>
<li>Name of the dataset (name of the Feature Class, table, file, etc.)</li>
<li>Authentication mode (e. g. <em>Operating System Authentication</em> or <em>Database Authentication</em>)</li>
<li>Username used to access the datasource (only in case of an Enterprise Geodatabase)</li>
</ul>
</li>
</ul>
<p>Now I had my information, but where to store them? Due to the experiences with <a href="https://www.elastic.co/products/elasticsearch">Elasticsearch</a>, I decided to use this product as my back end to store the data as a <em>search index</em>. Working with Elasticsearch means that you have to communicate with their REST API to create and modify the search index - just another <a href="https://www.safe.com/transformers/http-caller/">HttpCaller</a>-Transformer in the FME Workspace.</p>
<p>The last step was to build a small, lightweight client to access the collected information. For the sake of truth I’ve to admit that my comfort zone is the back end - I like to design, develop, test and stress REST APIs and stuff like that, but when it comes to client development, I always feel like a child on its first day of school. So I did a lot of research, checked out all the new frameworks, but finally I ended up with a combination of <a href="https://angularjs.org/">AngularJS</a> and <a href="http://getbootstrap.com/">Bootstrap</a>.<br />
The client is pretty simple: it communicates with Elasticsearch through the REST API. In the bootstrap phase, all the distinct values for type and environment of ArcGIS Server are read through <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations.html">Elasticsearch Aggregrations</a>. This enables to filter for specific types and environments with two drop-down lists.<br />
After doing a search, the user gets presented all Map Services matching the search criteria. In the detail view of a search result, all datasources are listed and the onces matching the search term are highlighted.</p>
<h2 id="time-for-a-demo">Time for a demo</h2>
<p>And that’s all! For those being curious to see and use the app, I’ve created a small demo: <a href="https://mu88.github.io/MapServiceSearch/index.html">https://mu88.github.io/MapServiceSearch</a><br />
Start exploring the sample data by entering the search term ‘tree’!</p>
<p>The source code as well as the FME Workspace to create the Elasticsearch index are available on <a href="https://github.com/mu88/MapServiceSearch">GitHub</a>. Feel free to use it, I’d appreciate if this little tool would also help others facing the same challenges.</p>
<p>Thanks for reading!</p>