Blog - Will Fuquahttps://fuqua.io/2022-10-23T00:00:00+07:00New major release of CSharpRepl 0.4.02022-10-23T00:00:00+07:002022-10-23T00:00:00+07:00Will Fuquatag:fuqua.io,2022-10-23:/blog/2022/10/new-major-release-of-csharprepl-040/<p>I’m happy to announce a new major release of <a class="reference external" href="https://github.com/waf/CSharpRepl">CSharpRepl</a>, a cross-platform command-line <a class="reference external" href="https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop"><span class="caps">REPL</span></a> for C#. This release is special because it almost entirely consists of contributions from the open-source community! In this post we’ll walk through a few of the new features.</p>
<p>If you haven’t heard …</p><p>I’m happy to announce a new major release of <a class="reference external" href="https://github.com/waf/CSharpRepl">CSharpRepl</a>, a cross-platform command-line <a class="reference external" href="https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop"><span class="caps">REPL</span></a> for C#. This release is special because it almost entirely consists of contributions from the open-source community! In this post we’ll walk through a few of the new features.</p>
<p>If you haven’t heard of CSharpRepl before and want to try it out, install it as a dotnet tool by running <tt class="docutils literal">dotnet tool install <span class="pre">-g</span> csharprepl</tt>, then running <tt class="docutils literal">csharprepl</tt> once the installation completes. If you’ve had it installed for a while already, update to the latest version by running <tt class="docutils literal">dotnet tool update <span class="pre">-g</span> csharprepl</tt>.</p>
<p>Special recognition and thanks goes to contributor <a class="reference external" href="https://github.com/waf/CSharpRepl/pulls?q=is%3Apr+author%3Akindermannhubert">@kindermannhubert</a>, who added the vast majority of these new features, and contributed countless bug fixes, performance improvements and usability enhancements.</p>
<div class="section" id="visual-studio-dark-theme-by-default">
<h2>Visual Studio Dark Theme by Default</h2>
<p>The default theme for CSharpRepl has changed from the terminal’s default colors to the Visual Studio Dark Theme:</p>
<div class="figure">
<a class="reference external image-reference" href="/img/csharprepl/csharprepl-darkmode.png"><img alt="csharprepl dark mode" src="/img/csharprepl/csharprepl-darkmode.png" style="width: 100%;" /></a>
<p class="caption">In the background, Visual Studio in dark mode. In the foreground, CSharpRepl with a matching theme.</p>
</div>
<p>This should provide a better out-of-the-box experience for most users, and more advanced users can either use the <tt class="docutils literal"><span class="pre">--useTerminalColorPalette</span></tt> flag to restore the previous behavior, or provide their own theme using a <a class="reference external" href="https://github.com/waf/CSharpRepl/blob/main/CSharpRepl/themes/dracula.json">theme.json</a> file.</p>
</div>
<div class="section" id="new-autocompletion-menu">
<h2>New Autocompletion Menu</h2>
<p>CSharpRepl now has a revamped autocompletion menu, with behavior closer to Visual Studio and Visual Studio Code. It also supports in-menu syntax-highlighting and navigating through method overloads with per-parameter context-sensitive help.</p>
<video controls style="width:100%; border-radius: 4px;">
<source src="/img/csharprepl/csharprepl-autocompletion.mp4" type="video/mp4">
Sorry, the current browser doesn’t support embedded <span class="caps">MP4</span> videos.
</video><p>In addition, if you provide the new <tt class="docutils literal"><span class="pre">--useUnicode</span></tt> parameter, the menu will use unicode glyphs to distinguish between properties, methods, events and other syntax types:</p>
<div class="figure">
<a class="reference external image-reference" href="/img/csharprepl/csharprepl-decorations.png"><img alt="csharprepl unicode glyphs in menu" src="/img/csharprepl/csharprepl-decorations.png" style="width: 100%;" /></a>
</div>
</div>
<div class="section" id="automatic-formatting-and-indentation-of-input">
<h2>Automatic formatting and indentation of input</h2>
<p>CSharpRepl has always supported multi-line editing, but the lack of automatic indentation and formatting could be frustrating. Now, the multi-line editing experience is much better: automatic indentation kicks in based on the nesting level of braces, and automatic formatting kicks in whenever a brace or semicolon is typed:</p>
<video controls style="width:100%; border-radius: 4px;">
<source src="/img/csharprepl/csharprepl-formatting.mp4" type="video/mp4">
Sorry, the current browser doesn’t support embedded <span class="caps">MP4</span> videos.
</video></div>
<div class="section" id="referencing-a-solution-now-references-all-projects">
<h2>Referencing a solution now references all projects</h2>
<p>Previously when loading a solution into CSharpRepl, only the final project and its dependencies were added as references. Now, all projects in the solution will be added as references. This should especially help when solutions have multiple projects that serve as entry points. Thanks <a class="reference external" href="https://github.com/Luiz-Ossinho">@Luiz-Ossinho</a> for this feature!</p>
</div>
<div class="section" id="new-configuration-file">
<h2>New configuration file</h2>
<p>CSharpRepl starts quickly and minimizes time-to-interactivity. However, that’s not worth much if you need to spend time typing a bunch of command line configuration options every time you launch the <span class="caps">REPL</span>! While the default configuration should work for most people, bespoke <span class="caps">REPL</span> setups aren’t uncommon.</p>
<p>Towards that end, CSharpRepl now supports a configuration file that is simply a list of command-line options, one per line, with optional comments. This file format (<a class="reference external" href="https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-response-files?view=vs-2022">.rsp</a>) is common to both msbuild.exe and csi.exe. Run <tt class="docutils literal">csharprepl <span class="pre">--configure</span></tt> to launch your editor opened to this configuration file.</p>
<p>Additionally, the new configuration file supports many more options in this release, including configurable keybindings.</p>
</div>
<div class="section" id="learning-more">
<h2>Learning More</h2>
<p>This is one of the larger releases CSharpRepl has ever had, and I’m excited for it. To learn more, <a class="reference external" href="https://github.com/waf/CSharpRepl">visit CSharpRepl on GitHub</a>!</p>
</div>
Enabling Command Line Completions with dotnet-suggest2021-09-04T00:00:00+07:002021-09-04T00:00:00+07:00Will Fuquatag:fuqua.io,2021-09-04:/blog/2021/09/enabling-command-line-completions-with-dotnet-suggest/<p>I recently removed the hand-written command line parser from <a class="reference external" href="https://github.com/waf/CSharpRepl">C# <span class="caps">REPL</span></a> and replaced it with the more standard System.CommandLine NuGet package. As part of this, it gained dotnet-suggest support. I couldn’t find much online discussion about dotnet-suggest, so I’m jotting down some notes here.</p>
<p>There are two …</p><p>I recently removed the hand-written command line parser from <a class="reference external" href="https://github.com/waf/CSharpRepl">C# <span class="caps">REPL</span></a> and replaced it with the more standard System.CommandLine NuGet package. As part of this, it gained dotnet-suggest support. I couldn’t find much online discussion about dotnet-suggest, so I’m jotting down some notes here.</p>
<p>There are two parts to dotnet-suggest:</p>
<ul class="simple">
<li>As an end-user, we configure our shell to enable dotnet-suggest shell completions.</li>
<li>As a developer, we use System.CommandLine to provide rich completions to our end users.</li>
</ul>
<p>We’ll be covering both parts in this blog post, as well as how it all works under the hood. At the end, we’ll look at an interesting way all this functionality is used (or abused?) in C# <span class="caps">REPL</span>.</p>
<div class="section" id="why-would-we-want-to-enable-dotnet-suggest">
<h2>Why would we want to enable dotnet-suggest?</h2>
<p>By configuring our system to take advantage of dotnet-suggest, we’ll unlock automatic command line completion for applications built with System.CommandLine. Quite a few applications in the dotnet ecosystem are built with System.CommandLine, and it’s especially prevalent in dotnet global tools. It’s cross-platform and works across Windows, Mac <span class="caps">OS</span>, and Linux.</p>
<p>For example, say we’re using the <tt class="docutils literal"><span class="pre">dotnet-trace</span></tt> tool to gather performance traces of a running process. If we’ve configured dotnet-suggest in our shell, we get the following experience:</p>
<video controls style="width:100%; border-radius: 4px;">
<source src="/img/dotnet-suggest/dotnet-trace-completions.mp4" type="video/mp4">
Sorry, the current browser doesn’t support embedded <span class="caps">MP4</span> videos.
</video><p>It’s a nice experience; we have our subcommands, command line flags, and any enumeration values tab-completed for us.</p>
<p>To enable this, we need to do a quick, one-time configuration of our shell:</p>
<ol class="arabic">
<li><p class="first">Install the dotnet-suggest global tool by running the following command:</p>
<div class="highlight"><pre><span></span><span class="go">> dotnet tool install -g dotnet-suggest</span>
</pre></div>
</li>
<li><p class="first">Add either <a class="reference external" href="https://github.com/dotnet/command-line-api/blob/main/src/System.CommandLine.Suggest/dotnet-suggest-shim.ps1">this PowerShell snippet</a> or <a class="reference external" href="https://github.com/dotnet/command-line-api/blob/main/src/System.CommandLine.Suggest/dotnet-suggest-shim.bash">this Bash snippet</a> to our shell configuration file. In PowerShell, our shell configuration file path is available in the <tt class="docutils literal">$profile</tt> variable, and for Bash or <span class="caps">ZSH</span> it’s <tt class="docutils literal"><span class="pre">~/.bash_profile</span></tt> or <tt class="docutils literal"><span class="pre">~/.zshrc</span></tt>, respectively.</p>
</li>
</ol>
<p>And we’re done! When we use applications written with System.CommandLine, like <a class="reference external" href="https://github.com/waf/CSharpRepl">C# <span class="caps">REPL</span></a> and <a class="reference external" href="https://docs.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-trace">dotnet-trace</a>, we can enjoy a first-rate tab completion experience.</p>
<p>Next, we’ll look at how to add dotnet-suggest support to our own tools. Spoiler, it’s trivial.</p>
</div>
<div class="section" id="using-system-commandline-and-dotnet-suggest-as-a-developer">
<h2>Using System.CommandLine and dotnet-suggest as a developer</h2>
<p>We’ll be using System.CommandLine to handle our command line parsing. A full tutorial on this library would get a bit lengthy, so we’ll only cover the very basics needed to add dotnet-suggest support. For a full walkthrough of System.CommandLine, see the <a class="reference external" href="https://github.com/dotnet/command-line-api#readme"><span class="caps">README</span></a>.</p>
<p>Despite System.CommandLine being around for a while now, it’s still listed as pre-release, so we’ll need to install it with the pre-release flag:</p>
<div class="highlight"><pre><span></span><span class="go">> dotnet add package System.CommandLine --prerelease</span>
</pre></div>
<p>Next, we’ll use System.CommandLine in our application to define and parse our application’s command line arguments:</p>
<ol class="arabic simple">
<li>Define a root command and its options.</li>
<li>Optionally define subcommands. This is useful when there are different sets of command line options. Using Git as an example, <tt class="docutils literal">git</tt> is a root command, and <tt class="docutils literal">git clone</tt> is a sub-command that takes a different set of options.</li>
<li>Pass your root command to a <tt class="docutils literal">CommandLineBuilder</tt>, which provides a fluent way to add functionality to your command. It can autogenerate <tt class="docutils literal"><span class="pre">--help</span></tt> and <tt class="docutils literal"><span class="pre">--version</span></tt> commands, as well as set up dotnet-suggest integration.</li>
<li>Define a callback that will invoke your app; the parameters of the callback correspond with the command line options you defined in Step 1.</li>
<li>Invoke the command line built by the <tt class="docutils literal">CommandLineBuilder</tt>, providing the args supplied to your program.</li>
</ol>
<p>A simple yet fully-working application might look like the following. It has autogenerated help and full dotnet-suggest support:</p>
<div class="highlight"><pre><span></span><span class="k">using</span><span class="w"> </span><span class="nn">System.CommandLine</span><span class="p">;</span><span class="w"></span>
<span class="k">using</span><span class="w"> </span><span class="nn">System.CommandLine.Builder</span><span class="p">;</span><span class="w"></span>
<span class="k">using</span><span class="w"> </span><span class="nn">System.CommandLine.Invocation</span><span class="p">;</span><span class="w"></span>
<span class="k">using</span><span class="w"> </span><span class="nn">System.CommandLine.Parsing</span><span class="p">;</span><span class="w"></span>
<span class="c1">// define our 3 command line parameters.</span>
<span class="c1">// the command can be invoked like MyApp --animal Cat --Emotion Normal "Hello"</span>
<span class="kt">var</span><span class="w"> </span><span class="n">rootCommand</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">RootCommand</span><span class="p">(</span><span class="s">"a little greeter app"</span><span class="p">)</span><span class="w"></span>
<span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">Option</span><span class="p"><</span><span class="kt">string</span><span class="p">>(</span><span class="s">"--animal"</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="n">getDefaultValue</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="s">"Cat"</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="n">description</span><span class="p">:</span><span class="w"> </span><span class="s">"Which animal should say the message"</span><span class="w"></span>
<span class="w"> </span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="c1">// we have suggestions for the animal, but do not constrain</span>
<span class="w"> </span><span class="c1">// it to only these options. We could also pass a function</span>
<span class="w"> </span><span class="c1">// to dynamically generate the suggestions.</span>
<span class="w"> </span><span class="p">.</span><span class="n">AddSuggestions</span><span class="p">(</span><span class="s">"Cat"</span><span class="p">,</span><span class="w"> </span><span class="s">"Dog"</span><span class="p">,</span><span class="w"> </span><span class="s">"Velociraptor"</span><span class="p">),</span><span class="w"></span>
<span class="w"> </span><span class="c1">// by using an enum, we constrain the options. We could also use</span>
<span class="w"> </span><span class="c1">// FromAmong(), similar to AddSuggestions() above, if we wanted</span>
<span class="w"> </span><span class="c1">// to constrain with Strings instead of an Enum.</span>
<span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">Option</span><span class="p"><</span><span class="n">Emotion</span><span class="p">>(</span><span class="s">"--emotion"</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="n">getDefaultValue</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="n">Emotion</span><span class="p">.</span><span class="n">Normal</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="n">description</span><span class="p">:</span><span class="w"> </span><span class="s">"How excited they should be when saying the message"</span><span class="w"></span>
<span class="w"> </span><span class="p">),</span><span class="w"></span>
<span class="w"> </span><span class="c1">// This is a positional argument; no command line flag is required.</span>
<span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">Argument</span><span class="p"><</span><span class="kt">string</span><span class="p">>(</span><span class="w"></span>
<span class="w"> </span><span class="s">"Message"</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="n">getDefaultValue</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="s">"Hello"</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="n">description</span><span class="p">:</span><span class="w"> </span><span class="s">"The message to say"</span><span class="w"></span>
<span class="w"> </span><span class="p">)</span><span class="w"></span>
<span class="p">};</span><span class="w"></span>
<span class="c1">// define our actual application. The callback arguments match the</span>
<span class="c1">// options and arguments defined above.</span>
<span class="n">rootCommand</span><span class="p">.</span><span class="n">Handler</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="n">CommandHandler</span><span class="p">.</span><span class="n">Create</span><span class="p">(</span><span class="w"></span>
<span class="w"> </span><span class="p">(</span><span class="kt">string</span><span class="w"> </span><span class="n">animal</span><span class="p">,</span><span class="w"> </span><span class="n">Emotion</span><span class="w"> </span><span class="n">emotion</span><span class="p">,</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="n">message</span><span class="p">)</span><span class="w"> </span><span class="p">=></span><span class="w"></span>
<span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="kt">var</span><span class="w"> </span><span class="n">output</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="err">$</span><span class="s">@"The {animal} says {message}"</span><span class="w"></span>
<span class="w"> </span><span class="p">+</span><span class="w"> </span><span class="n">emotion</span><span class="w"> </span><span class="k">switch</span><span class="w"></span>
<span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="n">Emotion</span><span class="p">.</span><span class="n">Normal</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="s">"."</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="n">Emotion</span><span class="p">.</span><span class="n">Excited</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="s">"!"</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="n">Emotion</span><span class="p">.</span><span class="n">Ecstatic</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="s">"!!!!!!!!!!"</span><span class="w"></span>
<span class="w"> </span><span class="p">};</span><span class="w"></span>
<span class="w"> </span><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">output</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="p">);</span><span class="w"></span>
<span class="c1">// set up common functionality like --help, --version, and dotnet-suggest support</span>
<span class="kt">var</span><span class="w"> </span><span class="n">commandLine</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">CommandLineBuilder</span><span class="p">(</span><span class="n">rootCommand</span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="p">.</span><span class="n">UseDefaults</span><span class="p">()</span><span class="w"> </span><span class="c1">// automatically configures dotnet-suggest</span>
<span class="w"> </span><span class="p">.</span><span class="n">Build</span><span class="p">();</span><span class="w"></span>
<span class="c1">// invokes our handler callback and actually runs our application</span>
<span class="k">await</span><span class="w"> </span><span class="n">commandLine</span><span class="p">.</span><span class="n">InvokeAsync</span><span class="p">(</span><span class="n">args</span><span class="p">);</span><span class="w"></span>
<span class="k">enum</span><span class="w"> </span><span class="n">Emotion</span><span class="w"></span>
<span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="n">Normal</span><span class="p">,</span><span class="w"> </span><span class="n">Excited</span><span class="p">,</span><span class="w"> </span><span class="n">Ecstatic</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</pre></div>
<p>The first time we run this program, the program will register itself with dotnet-suggest. Subsequent terminal windows will then be able to take advantage of the dotnet-suggest support automatically, assuming we’ve done the shell setup earlier in this post.</p>
<p>From a development perspective, we’re done! That’s all we have to do to enable dotnet-suggest in our application. For the remainder of this blog post, we’ll look into what’s going on “under the hood.”</p>
</div>
<div class="section" id="how-dotnet-suggest-works">
<h2>How dotnet-suggest works</h2>
<p>Ultimately, dotnet-suggest uses shell-specific functionality to provide its autocompletions. In PowerShell, for example, it uses <tt class="docutils literal"><span class="pre">Register-ArgumentCompleter</span></tt>. In this section, we’ll see how dotnet-suggest determines the completions it provides to these shell-specific hooks, but we won’t actually go into the shell-specific functionality.</p>
<p>In our above program, we called <tt class="docutils literal">.UseDefaults()</tt>. This function in turn called the following two functions (among others):</p>
<div class="highlight"><pre><span></span><span class="p">.</span><span class="n">RegisterWithDotnetSuggest</span><span class="p">()</span><span class="w"></span>
<span class="p">.</span><span class="n">UseSuggestDirective</span><span class="p">()</span><span class="w"></span>
</pre></div>
<p>Once we understand both these functions, we’ll fully understand how dotnet-suggest works!</p>
<p><strong>RegisterWithDotnetSuggest()</strong></p>
<p>As the name implies, this line will register our application with dotnet-suggest. Applications that are .<span class="caps">NET</span> Global Tools will be automatically discovered (by nature of being in the .<span class="caps">NET</span> Global Tool installation directory), but this line is needed when running our own binaries elsewhere on the filesystem.</p>
<p>Registration happens by writing to the <tt class="docutils literal"><span class="pre">~/.dotnet-suggest-registration.txt</span></tt> file. This file is simply a list of executables and their paths. It’s read by the code snippet we put in our shell profile, so dotnet-suggest doesn’t try to autocomplete every application on our system; only the ones that actually support it.</p>
<p>This registration only happens once; when registration is complete a file will be written to our filesystem, and future registrations will be skipped if this file already exists. On Windows, this file is in <tt class="docutils literal"><span class="pre">~/AppData/Local/Temp/system-commandline-sentinel-files</span></tt>. More generally, it’s in the path returned by <tt class="docutils literal">Path.GetTempPath()</tt>.</p>
<p><strong>UseSuggestDirective()</strong></p>
<p>This function allows dotnet-suggest to query our application for available commandline options. dotnet-suggest will send queries to our application as special command line parameters, and our application responds by writing to stdout (i.e. it uses Console.WriteLine).</p>
<p>We can see how this works by pretending to be dotnet-suggest and sending our own command line parameter queries. We’ll use what System.CommandLine calls a “directive” which is just a keyword surrounded by square brackets, used as in-band signalling:</p>
<div class="highlight"><pre><span></span><span class="go">> .\MyApp.exe [suggest]</span>
<span class="go">--animal</span>
<span class="go">--emotion</span>
<span class="go">--help</span>
<span class="go">--version</span>
<span class="go">-?</span>
<span class="go">-h</span>
<span class="go">/?</span>
<span class="go">/h</span>
</pre></div>
<p>We sent the <tt class="docutils literal">[suggest]</tt> directive, and our application returned the list of supported command line parameters. This is why we needed to implement our program as a callback function, so System.CommandLine could “own” the pipeline, and insert its own middleware.</p>
<p>We can also test completing substrings. Here, we’ll ask for completions of the string “<tt class="docutils literal"><span class="pre">--</span></tt>” when our caret position is at index 2:</p>
<div class="highlight"><pre><span></span><span class="go">> .\MyApp.exe [suggest:2] "--"</span>
<span class="go">--animal</span>
<span class="go">--emotion</span>
<span class="go">--help</span>
<span class="go">--version</span>
</pre></div>
<p>Asking for option values works the same way; here we typed <tt class="docutils literal"><span class="pre">--animal</span></tt> and the only completion that makes sense would be the required type of animal:</p>
<div class="highlight"><pre><span></span><span class="go">> .\MyApp.exe [suggest:9] "--animal "</span>
<span class="go">Cat</span>
<span class="go">Dog</span>
<span class="go">Velociraptor</span>
</pre></div>
<p>So, that about sums up how dotnet-suggest works. We register our application (or it’s auto registered), dotnet-suggest queries our application for available completions, and then uses our shell’s tab completion facility to supply these when we’re typing.</p>
</div>
<div class="section" id="a-fun-use-of-dotnet-suggest-in-c-repl">
<h2>A fun use of dotnet-suggest in C# <span class="caps">REPL</span></h2>
<p>The fact that dotnet-suggest will query our application for each tab completion request is pretty cool; it unlocks some interesting possibilities.</p>
<p>One neat usage in C# <span class="caps">REPL</span> is for the <tt class="docutils literal"><span class="pre">--using</span></tt> command line parameter. This parameter allows you to supply one or more C# namespaces to be included on startup of the <span class="caps">REPL</span>. For example, you might want to start the <span class="caps">REPL</span> with both <tt class="docutils literal">System.Collections.Immutable</tt> and <tt class="docutils literal">System.<span class="caps">IO</span>.Pipes</tt>. Since we can define a delegate to supply suggestions, we can easily allow tab completion of .<span class="caps">NET</span> namespaces from the command line!</p>
<video controls style="width:100%; border-radius: 4px;">
<source src="/img/dotnet-suggest/csharprepl-completions.mp4" type="video/mp4">
Sorry, the current browser doesn’t support embedded <span class="caps">MP4</span> videos.
</video><p>Another place I found it useful was for the <tt class="docutils literal"><span class="pre">--framework</span></tt> command line option; this parameter needs to be a <a class="reference external" href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/metapackage-app?view=aspnetcore-5.0">.<span class="caps">NET</span> Shared Framework</a> that is installed on the local computer. Rather than making the user go figure out what shared frameworks are locally installed, C# <span class="caps">REPL</span> can simply query on behalf of the user, and allow them to be easily tab-completed.</p>
</div>
Analyzing Code Quality with LINQ and NDepend2021-08-30T00:00:00+07:002021-08-30T00:00:00+07:00Will Fuquatag:fuqua.io,2021-08-30:/blog/2021/08/analyzing-code-quality-with-linq-and-ndepend/<p>I’ve been building an open-source personal project (<a class="reference external" href="https://github.com/waf/CSharpRepl">C# <span class="caps">REPL</span></a>) and spending a good chunk of time on code quality. I self-impose a zero-warning policy, use nullable reference types, track unit test coverage, etc.</p>
<p>After ensuring all the Visual Studio / Roslyn code analyzer warnings were fixed, I thought I’d …</p><p>I’ve been building an open-source personal project (<a class="reference external" href="https://github.com/waf/CSharpRepl">C# <span class="caps">REPL</span></a>) and spending a good chunk of time on code quality. I self-impose a zero-warning policy, use nullable reference types, track unit test coverage, etc.</p>
<p>After ensuring all the Visual Studio / Roslyn code analyzer warnings were fixed, I thought I’d try out NDepend to get a second opinion, and also understand its capabilities. After downloading <a class="reference external" href="https://www.ndepend.com/download">a free trial of NDepend</a> and spending some time with it, I was pretty impressed with its technical underpinnings as they’re exposed to the end user. Spoilers: <em>It’s <span class="caps">LINQ</span> all the way down</em>.</p>
<div class="section" id="static-analysis-with-linq">
<h2>Static Analysis with <span class="caps">LINQ</span></h2>
<p>Out of the box, there’s a large collection of static analysis rules; they’re mostly focused on finding software design flaws. For example, it includes a rule that warns against assigning a field from too many methods; it’s a sign that there’s some bug-prone mutation, and a better design could be possible:</p>
<img alt="A rule UI that says not to assign a field from many methods. It's a symptom of bug-prone code." class="align-center" src="/img/ndepend/1-too-many-methods.png" style="width: 90%;" />
<p>This screen may look like a typical static analysis rule, but there’s more going on under-the-hood. If we click the <cite>View Source Code</cite> button, we’ll see the following, editable rule code:</p>
<div class="figure align-center">
<a class="reference external image-reference" href="https://www.ndepend.com/default-rules/NDepend-Rules-Explorer.html?ruleid=ND1906"><img alt="A LINQ statement. warnif count > 0 from f in JustMyCode.Fields where !f.IsEnumValue && !f.IsImmutable && !f.IsInitOnly && !f.IsGeneratedByCompiler && !f.IsEventDelegateObject let methodsAssigningMe = f.MethodsAssigningMe.Where(m => !m.IsConstructor) where methodsAssigningMe.Count() >= (!f.IsStatic ? 4 : 2) select new { f, methodsAssigningMe, f.MethodsReadingMeButNotAssigningMe, f.MethodsUsingMe, Debt = (4+(f.IsStatic ? 10 : 5)).ToMinutes().ToDebt(), Severity = Severity.High}" src="/img/ndepend/2-too-many-methods-linq.png" style="width: 90%;" /></a>
</div>
<small class="align-center" style="display:block;color:#888"><a href="https://www.ndepend.com/default-rules/NDepend-Rules-Explorer.html?ruleid=ND1906">Rule <span class="caps">ND1906</span></a> – click to view the full rule description and code</small><p>Hey, that’s <span class="caps">LINQ</span>!</p>
<p>Technically, it’s called <a class="reference external" href="https://www.ndepend.com/docs/cqlinq-syntax#Introduction">CQLinq</a> (Code Query <span class="caps">LINQ</span>), which is <span class="caps">LINQ</span> with additional static analysis keywords, and running over an object model that represents a codebase. Conceptually, it’s similar to Roslyn’s syntax/semantic model, but with a bunch of nice affordances and predefined properties that make it simpler to do solution-wide static analysis. The CQLinq object model represents an entire application.</p>
<p>For example, here’s a predefined calculation for cyclomatic complexity. It runs across all methods in the application using the <tt class="docutils literal">Application.Methods</tt> property; there’s no need to recursively find all files or classes:</p>
<img alt="from m in Application.Methods where m.CyclomaticComplexity > 20 && !m.IsAbstract orderby m.CyclomaticComplexity descending select new { m, m.CyclomaticComplexity }" class="align-center" src="/img/ndepend/3-cyclomatic-complexity-query.png" style="width: 90%;" />
<p>Notice that the rule uses a <tt class="docutils literal">CyclomaticComplexity</tt> property that already exists on the method. We can use intellisense to explore other properties in the CQLinq object model:</p>
<img alt="The CQLinq query editor open, with an intellisense menu showing properties like ReadsMutableObjectState, PercentageCoverage, PercentageComment, ShouldBePublic, and many more." class="align-center" src="/img/ndepend/4-intellisense.png" style="width: 94%;" />
</div>
<div class="section" id="analyzing-code-evolution-over-time-with-linq">
<h2>Analyzing code evolution over time with <span class="caps">LINQ</span></h2>
<p>CQLinq also supports comparing the current version of the code with past versions of the code. Analyzing code once will create a <em>baseline;</em> future analyses can refer to this baseline with the <tt class="docutils literal">.OlderVersion()</tt> helper method. Here’s how we can identify mutability being introduced as part of a code change:</p>
<a class="reference external image-reference" href="https://www.ndepend.com/default-rules/NDepend-Rules-Explorer.html?ruleid=ND1108"><img alt="A CQLinq statement that reads: Avoid transforming an immutable type into a mutable one. warnif count > 0 from t in Application.Types where t.CodeWasChanged() && t.OlderVersion().IsImmutable && !t.IsImmutable && !t.IsStatic let culpritFields = t.InstanceFields.Where(f => !f.IsImmutable) select new { t, culpritFields, Debt = (10 + 10*culpritFields.Count()).ToMinutes().ToDebt(), Severity = Severity.High }" class="align-center" src="/img/ndepend/5-baseline-mutability-detection.png" style="width: 90%;" /></a>
<small class="align-center" style="display:block;color:#888"><a href="https://www.ndepend.com/default-rules/NDepend-Rules-Explorer.html?ruleid=ND1108">Rule <span class="caps">ND1108</span></a> – click to view the full rule description and code</small><p>There are a <a class="reference external" href="https://www.ndepend.com/default-rules/NDepend-Rules-Explorer.html?ruleid=ND1105#!">bunch of prebuilt rules</a> that take advantage of this baseline-comparison feature. For example, we can define and identify breaking changes in a code change to a library, so we don’t accidentally break downstream users, and can better manage <abbr title="Semantic Versioning">semver</abbr>:</p>
<a class="reference external image-reference" href="https://www.ndepend.com/default-rules/NDepend-Rules-Explorer.html?ruleid=ND1500"><img alt="A CQLinq statement that fades out to white. warnif count > 0 from m in codeBase.OlderVersion().Application.Methods where m.IsPubliclyVisible && ((m.WasRemoved() && !m.ParentType.WasRemoved() && !m.IsObsolete) || (!m.WasRemoved() && !m.NewerVersion().IsPubliclyVisible && m.ParentType.NewerVersion().IsPubliclyVisible) || (!m.WasRemoved() && m.ReturnType != null && m.NewerVersion().ReturnType != null && m.ReturnType.FullName != m.NewerVersion().ReturnType.FullName))" class="align-center" src="/img/ndepend/6-breaking-changes.png" style="width: 100%;" /></a>
<small class="align-center" style="display:block;color:#888"><a href="https://www.ndepend.com/default-rules/NDepend-Rules-Explorer.html?ruleid=ND1500">Rule <span class="caps">ND1500</span></a> – click to view the full rule description and code</small></div>
<div class="section" id="identifying-high-severity-issues-with-linq">
<h2>Identifying high-severity issues with <span class="caps">LINQ</span></h2>
<p>The CQLinq object model doesn’t just make our application source code queryable, it also makes static analysis rules and their violations queryable as well. This gets a bit meta; by using the <tt class="docutils literal">Issues</tt> object model we can query for violations of rules defined by other <span class="caps">LINQ</span> statements:</p>
<img alt="from issue in Issues where issue.Severity > Severity.Info orderby issue.Severity descending select new { issue, issue.Severity, issue.CodeElement }" class="align-center" src="/img/ndepend/7-issues-query.png" style="width: 90%;" />
<p>This can be combined with the previous section on code evolution to find new issues, or get an idea of how issues are being introduced or resolved over time. We can use the <tt class="docutils literal">failif</tt> CQLinq keyword to set hard limits based on our issues and rules; these are known as <cite>Quality Gates</cite> and are useful for continuous integration scenarios. The syntax is the same as the <tt class="docutils literal">warnif</tt> keyword we saw in the earlier CQLinq rules.</p>
</div>
<div class="section" id="using-the-ui-for-parameterized-linq-queries">
<h2>Using the <span class="caps">UI</span> for parameterized <span class="caps">LINQ</span> queries</h2>
<p>So far we’ve been focused on the code editor, but there’s also a pretty slick <span class="caps">UI</span>; by setting up our <span class="caps">LINQ</span> statements to have parameters, we can explore ranges of these parameters in a live-updating, auto-generated <span class="caps">UI</span>:</p>
<img alt="A UI with several form elements, like input fields, dropdown lists, and sliders. Each form element corresponds to a highlighted placeholder in the LINQ query." class="align-center" src="/img/ndepend/8-coupling-queries.png" style="width: 90%;" />
<p>The NDepend <span class="caps">UI</span> has a lot of prebuilt views for understanding, categorizing and prioritizing issues. In addition to the typical column views that we’ve seen in this post, they also have some more <a class="reference external" href="https://www.ndepend.com/docs/visual-studio-dependency-graph">advanced graphical dependency views</a> that are also powered by CQLinq. All the typical static analysis workflows can be done via the <span class="caps">UI</span> if you don’t want to get into the <span class="caps">LINQ</span> side of things—but where’s the fun in that?</p>
<p>If the command line is your preferred form of <span class="caps">UI</span>, there’s also a command line runner for all these CQLinq statements, which is also useful for integrating with other software.</p>
</div>
<div class="section" id="summary">
<h2>Summary</h2>
<p>This blog post focused solely on the CQLinq part of NDepend, but there’s a bunch more <a class="reference external" href="https://www.ndepend.com/default-rules/NDepend-Rules-Explorer.html">rules</a>, <a class="reference external" href="https://www.ndepend.com/sample-reports/">reports</a>, and <a class="reference external" href="https://www.ndepend.com/features/code-complexity#Diagrams">data visualizations</a> available.</p>
<p>I’ve personally always been interested in static analysis tools, programming language syntax trees, and <span class="caps">LINQ</span>; so it was great to see NDepend use CQLinq to combine all three into a well-packaged static analysis tool.</p>
<p>More practically, it helped me identify a good set of fixes for C# <span class="caps">REPL</span>. I was able to refactor towards immutability, better structure some confusing namespaces, and improve the general design of the application.</p>
</div>
Tips for using the Windows Command Line effectively2021-05-10T00:00:00+07:002021-05-10T00:00:00+07:00Will Fuquatag:fuqua.io,2021-05-10:/blog/2021/05/tips-for-using-the-windows-command-line-effectively/<p>Although the command line experience on Windows has historically been less than stellar, the last few years have seen rapid improvement. In this post, I’ll cover three main areas of improvement:</p>
<ol class="arabic simple">
<li>The new Windows Terminal</li>
<li>Running PowerShell Core</li>
<li>Developing with Windows Subsystem for Linux</li>
</ol>
<p>In case you’re familiar …</p><p>Although the command line experience on Windows has historically been less than stellar, the last few years have seen rapid improvement. In this post, I’ll cover three main areas of improvement:</p>
<ol class="arabic simple">
<li>The new Windows Terminal</li>
<li>Running PowerShell Core</li>
<li>Developing with Windows Subsystem for Linux</li>
</ol>
<p>In case you’re familiar with all three of these topics, I’ll be including some concrete tips in each topic that are real timesavers; hopefully at least one of the tips will be new.</p>
<div class="section" id="the-new-windows-terminal">
<h2>The new Windows Terminal</h2>
<p>Microsoft’s release of Windows Terminal, a modern, <span class="caps">GPU</span>-accelerated and open-source terminal application, is a real game changer. It can be installed and updated <a class="reference external" href="ms-windows-store://pdp/?ProductId=9n0dx20hk701">from the Microsoft store</a>, with other methods of installation available <a class="reference external" href="https://github.com/microsoft/terminal">on GitHub</a>.</p>
<p>Windows Terminal supports running various shells in tabs (and side-by-side panes). For example, we can use PowerShell in one tab, <span class="caps">ZSH</span> under the Windows Subsystem for Linux in another tab, and to the side, a Command Prompt <span class="strike">pain</span> pane.</p>
<p>Windows Terminal is ultra-configurable, and uses <span class="caps">JSON</span> for its configuration store (with a work-in-progress Settings <span class="caps">UI</span> layered on top). An example of this configurability is the <tt class="docutils literal">sendInput</tt> command, which can send arbitrary keystrokes and escape sequences to the terminal:</p>
<p>For example, we could bind a frequently used <tt class="docutils literal">git log</tt> command to <cite>Ctrl+Alt+L</cite>:</p>
<div class="highlight"><pre><span></span><span class="c1">// place this keybinding in the `actions` array</span><span class="w"></span>
<span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nt">"command"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nt">"action"</span><span class="p">:</span><span class="w"> </span><span class="s2">"sendInput"</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">"input"</span><span class="p">:</span><span class="w"> </span><span class="s2">"git log --all --decorate --oneline --graph\r\n"</span><span class="w"></span>
<span class="w"> </span><span class="p">},</span><span class="w"></span>
<span class="w"> </span><span class="nt">"keys"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ctrl+alt+l"</span><span class="w"></span>
<span class="p">},</span><span class="w"></span>
</pre></div>
<p>Git, of course, has its own aliasing system, but it’s nice to have a keyboard shortcut for it, too. I personally also keybind some frequent directory navigation commands, as well as some common compiler / build tool commands.</p>
</div>
<div class="section" id="running-powershell-core">
<h2>Running PowerShell Core</h2>
<p>When most people think of PowerShell, they probably think of the older PowerShell 5, which is the default version of PowerShell installed on Windows. This version of PowerShell is <strong>old</strong> (the most recent point release was 5.1, in 2017).</p>
<p>PowerShell Core, the cross-platform, open-source alternative, has been around since 2016 and is actively developed. It runs alongside PowerShell 5, instead of replacing it, so there’s no risk in installing it. It can be installed from the <a class="reference external" href="ms-windows-store://pdp/?ProductId=9mz1snwt0n5d">Microsoft Store</a> or <a class="reference external" href="https://github.com/PowerShell/PowerShell">GitHub</a>.</p>
<p>PowerShell Core has a lot of nice improvements across the usability of its shell, its programming language, and its performance. It’s worth upgrading just for the <tt class="docutils literal">cd -</tt> and <tt class="docutils literal">cd +</tt> features, which navigate back and forward through the working directory history:</p>
<div class="highlight"><pre><span></span><span class="gp">will@home:~$ </span><span class="nb">cd</span> projects/death-ray
<span class="gp">will@home:~/projects/death-ray$ </span><span class="nb">cd</span> node_modules
<span class="gp">will@home:~/projects/death-ray/node_modules$ </span><span class="nb">cd</span> -
<span class="gp">will@home:~/projects/death-ray$ </span><span class="nb">cd</span> -
<span class="gp">will@home:~$ </span><span class="nb">cd</span> +
<span class="gp">will@home:~/projects/death-ray$</span>
</pre></div>
</div>
<div class="section" id="developing-with-windows-subsystem-for-linux">
<h2>Developing with Windows Subsystem for Linux</h2>
<p>Now, it might be odd for a “Windows Command Line” blog post to recommend installing Linux, but, well, here we are. The Linux / <span class="caps">BSD</span> / Mac <span class="caps">OS</span> communities have been setting the standard in command line efficiency, and we can get all that goodness on Windows 10, too.</p>
<p>The Windows Subsystem for Linux 2 (<span class="caps">WSL2</span>) provides a real Linux command line inside Windows, with Linux kernel updates shipped via Windows Update (!!!). After <a class="reference external" href="https://docs.microsoft.com/en-us/windows/wsl/install-win10">installing <span class="caps">WSL2</span></a>, we can download distros like <a class="reference external" href="ms-windows-store://pdp/?ProductId=9nblggh4msv6">Ubuntu from the Microsoft Store</a>. From there, advanced shells like <span class="caps">ZSH</span> and Fish are just an <tt class="docutils literal">apt install</tt> away!</p>
<p>While the typical <span class="caps">GNU</span>/Linux command line tools, like <tt class="docutils literal">grep</tt> and <tt class="docutils literal">sed</tt>, are great, we can make our experience even better. When we run the <tt class="docutils literal">code</tt> command inside <span class="caps">WSL2</span>, it will launch Visual Studio Code on Windows, and set up a client/server bridge to <span class="caps">WSL2</span> automatically. This allows us to use the Visual Studio Code front-end on Windows, with all the <span class="caps">IDE</span> / editor features it supports, and it will communicate with our <span class="caps">WSL2</span> backend to actually execute the program.</p>
<p>This way, we can use the <span class="caps">WSL2</span> command line from Windows Terminal, with our code executing under <span class="caps">WSL2</span>, but we get a graphical editing and debugging experience:</p>
<img alt="" src="/img/windows-terminal-vscode-with-wsl2.png" style="width: 100%;" />
<p>This is especially useful when doing development in languages where Windows is a bit of a second-class citizen, like on NodeJS or Python.</p>
</div>
<div class="section" id="enjoy-this-you-can-get-more">
<h2>Enjoy this? You can get more!</h2>
<p>You can find all these tips and many more in my new book, <a class="reference external" href="https://packt.live/3gon3St">Windows Terminal Tips, Tricks, and Productivity Hacks</a>, which is currently $10 off on Amazon! Thanks for reading.</p>
<embed>
<style>
.strike { text-decoration: line-through; }
li { margin: 8px 0 8px 0; }
</style>
</embed></div>
A Lesser-Known C# Feature: Nested Object Initializers2020-12-12T00:00:00+07:002020-12-12T00:00:00+07:00Will Fuquatag:fuqua.io,2020-12-12:/blog/2020/12/a-lesser-known-csharp-feature-nested-object-initializers/<p>I’ve been writing C# for quite some time now, but only recently found out about the “nested object initializers” syntax in C#. Nested object initializers elegantly solve problems for which I’ve previously used workarounds. It’s not a new feature; it was introduced in C# 3.0, under …</p><p>I’ve been writing C# for quite some time now, but only recently found out about the “nested object initializers” syntax in C#. Nested object initializers elegantly solve problems for which I’ve previously used workarounds. It’s not a new feature; it was introduced in C# 3.0, under section 7.5.10.2 of the language specification:</p>
<blockquote>
An object initializer after the equals sign is a nested object initializer, i.e. an initialization of an embedded object. Instead of assigning a new value to the field or property, the assignments in the nested object initializer are treated as assignments to members of the field or property.</blockquote>
<p>In case the above specification is not clear, we’ll work through an example. C# has several types of initializer syntax, among which are object initializers and collection initializers. Nested object initializers could be considered a specialization of both.</p>
<div class="section" id="reviewing-object-and-collection-initializers">
<h2>Reviewing Object and Collection Initializers</h2>
<p>Before diving into nested object initializers, let’s review object initializers and collection initializers. These initializers provide a convenient syntax for object construction; they technically construct an “empty” object and then mutate that object as part of the initialization. Here’s an example of both:</p>
<div class="highlight"><pre><span></span><span class="c1">// given classes defined like this:</span>
<span class="k">class</span><span class="w"> </span><span class="nc">Company</span><span class="w"></span>
<span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="k">public</span><span class="w"> </span><span class="n">Person</span><span class="w"> </span><span class="n">Ceo</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">get</span><span class="p">;</span><span class="w"> </span><span class="k">set</span><span class="p">;</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="k">public</span><span class="w"> </span><span class="n">IList</span><span class="p"><</span><span class="n">Person</span><span class="p">></span><span class="w"> </span><span class="n">Employees</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">get</span><span class="p">;</span><span class="w"> </span><span class="k">set</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="k">class</span><span class="w"> </span><span class="nc">Person</span><span class="w"></span>
<span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="k">public</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="n">Name</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">get</span><span class="p">;</span><span class="w"> </span><span class="k">set</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="c1">// an object initializer for Company</span>
<span class="kt">var</span><span class="w"> </span><span class="n">apple</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">Company</span><span class="w"></span>
<span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="c1">// an object initializer for Person</span>
<span class="w"> </span><span class="n">Ceo</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">Person</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">Name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s">"Tim Cook"</span><span class="w"> </span><span class="p">},</span><span class="w"></span>
<span class="w"> </span><span class="c1">// a collection initializer:</span>
<span class="w"> </span><span class="n">Employees</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">List</span><span class="p"><</span><span class="n">Person</span><span class="p">></span><span class="w"></span>
<span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">Person</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">Name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s">"Joe"</span><span class="w"> </span><span class="p">},</span><span class="w"></span>
<span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">Person</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">Name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s">"Janet"</span><span class="w"> </span><span class="p">},</span><span class="w"></span>
<span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">Person</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">Name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s">"John"</span><span class="w"> </span><span class="p">},</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="p">};</span><span class="w"></span>
</pre></div>
<p>In the above example, the object initializer is used to initialize both the <tt class="docutils literal">Company</tt> object and the <tt class="docutils literal">Person</tt> objects. The collection initializer is used to create a new <tt class="docutils literal">List<Person></tt> and add 3 entries to it. Collection initializers can initialize any object that implements <tt class="docutils literal">IEnumerable</tt> and contains an <tt class="docutils literal">Add</tt> method (either as part of the type, or as an extension method).</p>
</div>
<div class="section" id="nested-object-initializers">
<h2>Nested Object Initializers</h2>
<p>Now, how do nested object initializers tie into this? In the previous code example, we were creating new objects (a new <tt class="docutils literal">Person</tt> for the <tt class="docutils literal">Company.Ceo</tt> property, and a new <tt class="docutils literal">List<Person></tt> for the <tt class="docutils literal">Company.Employees</tt> property). Nested object initializers allow for <strong>mutating default values</strong> in the class.</p>
<p>Let’s alter our class definition above. We all know that null values are a pain, so we could set default values for each property, and use nested object initializers during object construction:</p>
<div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">Company</span><span class="w"></span>
<span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="k">public</span><span class="w"> </span><span class="n">Person</span><span class="w"> </span><span class="n">Ceo</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">get</span><span class="p">;</span><span class="w"> </span><span class="k">set</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="k">new</span><span class="w"> </span><span class="n">Person</span><span class="p">();</span><span class="w"></span>
<span class="w"> </span><span class="k">public</span><span class="w"> </span><span class="n">IList</span><span class="p"><</span><span class="n">Person</span><span class="p">></span><span class="w"> </span><span class="n">Employees</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">get</span><span class="p">;</span><span class="w"> </span><span class="k">set</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="k">new</span><span class="w"> </span><span class="n">List</span><span class="p"><</span><span class="n">Person</span><span class="p">>();</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
<span class="kt">var</span><span class="w"> </span><span class="n">apple</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">Company</span><span class="w"></span>
<span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="n">Ceo</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">Name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s">"Tim Cook"</span><span class="w"> </span><span class="p">},</span><span class="w"></span>
<span class="w"> </span><span class="n">Employees</span><span class="w"> </span><span class="p">=</span><span class="w"></span>
<span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">Person</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">Name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s">"Joe"</span><span class="w"> </span><span class="p">},</span><span class="w"></span>
<span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">Person</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">Name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s">"Janet"</span><span class="w"> </span><span class="p">},</span><span class="w"></span>
<span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">Person</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">Name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s">"John"</span><span class="w"> </span><span class="p">},</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</pre></div>
<p>With the nested object initializer syntax, we remove the reference to the constructors for both properties, and it will mutate the existing, default value.</p>
</div>
<div class="section" id="where-is-this-useful">
<h2>Where is this useful?</h2>
<p>The nested object initializer implies mutation of existing values, so it shouldn’t be used everywhere; I prefer immutability where possible. However, there are still a few areas where this could be useful.</p>
<p>For nested object initializers with collection initializers, this helps resolve a conflict between wanting to use object initializers, and wanting to <a class="reference external" href="https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/guidelines-for-collections#collection-properties-and-return-values">prefer empty collections over null collections</a>. This was always a bit of a conflict for me.</p>
<p>For nested object initializers with object initializers, this could help in configuration scenarios, where we want to have some default configuration object. In this case, the nested object initializer could be used to override these default configuration values.</p>
</div>
<div class="section" id="learning-more">
<h2>Learning more</h2>
<p>The best documentation for this seems to be the <a class="reference external" href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/expressions#object-initializers">C# Language Reference</a>. There’s also a <a class="reference external" href="https://github.com/dotnet/docs/issues/12979">GitHub issue</a> asking for better documentation on this feature. I personally found the chat rooms available on gitter very helpful for learning more about this feature; special thanks to <a class="reference external" href="https://github.com/HaloFour">HaloFour</a>, <a class="reference external" href="https://github.com/Joe4evr">Joe4evr</a> and <a class="reference external" href="https://github.com/jnm2">jnm2</a> for helping out!</p>
</div>
Microsoft Build 2020 – Highlights for .NET Developers2020-05-22T00:00:00+07:002020-05-22T00:00:00+07:00Will Fuquatag:fuqua.io,2020-05-22:/blog/2020/05/microsoft-build-2020-highlights-for-net-developers/<p>Over the course of the last three days, Microsoft Build 2020 released a flood of news and announcements.
For those of us who follow the .<span class="caps">NET</span> ecosystem, it can be difficult to wade through them all!</p>
<p>I’ve collected a list of announcements that I think are interesting as a …</p><p>Over the course of the last three days, Microsoft Build 2020 released a flood of news and announcements.
For those of us who follow the .<span class="caps">NET</span> ecosystem, it can be difficult to wade through them all!</p>
<p>I’ve collected a list of announcements that I think are interesting as a .<span class="caps">NET</span> developer, and added short
summaries. The announcements are grouped into four categories: <span class="caps">ASP</span>.<span class="caps">NET</span>, .<span class="caps">NET</span>, Visual Studio and Windows.</p>
<p>In addition, each category is split into “released” (you can use it now!) or “preview / announced”
(you can test it out now, or soon).</p>
<div class="section" id="asp-net">
<h2><span class="caps">ASP</span>.<span class="caps">NET</span></h2>
<div class="section" id="released">
<h3>Released</h3>
<ul class="simple">
<li><a class="reference external" href="https://devblogs.microsoft.com/aspnet/blazor-webassembly-3-2-0-now-available/">Blazor WebAssembly is released!</a> - v3.2.0 is an official release – it’s not a preview anymore! It’s considered ready for production use, but is not a Long Term Support release.</li>
<li><a class="reference external" href="https://devblogs.microsoft.com/aspnet/introducing-project-tye/">Project Tye</a> - a tool for deploying .<span class="caps">NET</span> core applications to Kubernetes.</li>
</ul>
</div>
<div class="section" id="preview-announced">
<h3>Preview / Announced</h3>
<ul class="simple">
<li><a class="reference external" href="https://devblogs.microsoft.com/aspnet/asp-net-core-updates-in-net-5-preview-4/"><span class="caps">ASP</span>.<span class="caps">NET</span> Core updates in .<span class="caps">NET</span> 5 Preview 4</a> - Improved <span class="caps">HTTP</span>/2 performance by adding support for HPack dynamic compression. Smaller docker image sizes by sharing layers between upstream images.</li>
</ul>
</div>
</div>
<div class="section" id="net">
<h2>.<span class="caps">NET</span></h2>
<div class="section" id="preview-announced-1">
<h3>Preview / Announced</h3>
<ul class="simple">
<li><a class="reference external" href="https://devblogs.microsoft.com/dotnet/announcing-net-5-preview-4-and-our-journey-to-one-net/">.<span class="caps">NET</span> 5 Preview 4</a> - C# 9 and F# 5 previews, source generators for compile-time code generation / introspection. Also some updates on publishing to a single file application. <span class="caps">ARM</span> processor support for Web / <span class="caps">UI</span> applications.</li>
<li><a class="reference external" href="https://devblogs.microsoft.com/dotnet/welcome-to-c-9-0/">C# 9 Preview</a> - A lot of new functional features. This is just a preview – features listed in this article might not make it into the final release, or might have different syntax after all the design issues have been figured out. But it’s still exciting to see C# embrace immutability!</li>
<li><a class="reference external" href="https://devblogs.microsoft.com/dotnet/f-5-update-for-net-5-preview-4/">F# 5 Preview</a> - Better tooling, faster compiling, interoperability with C#’s new “default interface member” feature.</li>
<li><a class="reference external" href="https://devblogs.microsoft.com/dotnet/introducing-net-multi-platform-app-ui/">As part of .<span class="caps">NET</span> 5, Xamarin.Forms library is now called <span class="caps">MAUI</span></a> - My guess is everyone will continue to call it Xamarin.Forms. This article also mentions that Xamarin.iOS and Xamarin.Android will be integrated into .<span class="caps">NET</span> 6.</li>
<li><a class="reference external" href="https://devblogs.microsoft.com/dotnet/introducing-yarp-preview-1/"><span class="caps">YARP</span> Reverse Proxy Preview</a> - High performance reverse proxy server. Think of it like an application load balancer / rewrite rules engine built as <span class="caps">ASP</span>.<span class="caps">NET</span> Core middleware.</li>
<li><a class="reference external" href="https://github.com/microsoft/ProjectReunion">Project Reunion</a> - A polyfill library so you can use the same libraries (WinUI 3, WebView2, <span class="caps">MSIX</span>) on both WinForms and <span class="caps">UWP</span> platforms.</li>
</ul>
</div>
</div>
<div class="section" id="visual-studio">
<h2>Visual Studio</h2>
<div class="section" id="released-1">
<h3>Released</h3>
<ul class="simple">
<li><a class="reference external" href="https://devblogs.microsoft.com/visualstudio/visual-studio-2019-v16-6-and-v16-7-preview-1-ship-today/"><span class="caps">VS2019</span> 16.6 released, with 16.7 in preview</a> - <span class="caps">VS2019</span> 16.6 contains a new “.<span class="caps">NET</span> Async Tool” for debugging applications. Better snapshot debugging and refactorings. Has a new “Web Tools for Azure” tool, as well as new C++ 20 standard library features.</li>
<li><a class="reference external" href="https://devblogs.microsoft.com/dotnet/windows-forms-designer-for-net-core-released/">Windows Forms Designer for .<span class="caps">NET</span> Core</a> - The WinForms designer now works with .<span class="caps">NET</span> Core WinForms applications.</li>
<li><a class="reference external" href="https://devblogs.microsoft.com/dotnet/ml-net-model-builder-is-now-a-part-of-visual-studio/"><span class="caps">ML</span>.<span class="caps">NET</span> Model Builder in Visual Studio</a> - The existing <span class="caps">ML</span>.<span class="caps">NET</span> Model Builder extension (which is super cool) is now integrated into Visual Studio. Add “Machine Learning” with just two clicks! …plus years of study.</li>
</ul>
</div>
<div class="section" id="preview-announced-2">
<h3>Preview / Announced</h3>
<ul class="simple">
<li><a class="reference external" href="https://devblogs.microsoft.com/visualstudio/expanding-visual-studio-2019-support-for-visual-studio-codespaces/">Visual Studio 2019 can be used with Codespaces</a> - Visual Studio Online is renamed to Visual Studio Codespaces. Visual Studio 2019 can now serve as a “client ui” to a codespace in the cloud.</li>
<li><a class="reference external" href="https://devblogs.microsoft.com/dotnet/using-visual-studio-codespaces-with-net-core/">How to use .<span class="caps">NET</span> Core with Visual Studio Codespaces</a> - A tour of the editing / testing / debugging experience of .<span class="caps">NET</span> Core with Visual Studio <span class="strike">Online</span> Codespaces.</li>
</ul>
</div>
</div>
<div class="section" id="windows">
<h2>Windows</h2>
<div class="section" id="released-2">
<h3>Released</h3>
<ul class="simple">
<li><a class="reference external" href="https://devblogs.microsoft.com/commandline/windows-terminal-1-0/">The new Windows Terminal hit 1.0</a> - Supports tabs and split panes. <span class="caps">GPU</span> accelerated rendering will display your compile errors blazing fast.</li>
</ul>
</div>
<div class="section" id="preview-announced-3">
<h3>Preview / Announced</h3>
<ul class="simple">
<li><a class="reference external" href="https://devblogs.microsoft.com/commandline/windows-package-manager-preview/">winget - a new windows package manager</a> - Windows command line package manager like chocolately. As far as I can tell, it does not manage dependencies or updates (yet?).</li>
<li><a class="reference external" href="https://devblogs.microsoft.com/commandline/the-windows-subsystem-for-linux-build-2020-summary/">Windows Subsystem For Linux (<span class="caps">WSL</span>) 2</a> - <span class="caps">WSL2</span> will ship later this month in the Windows 10 May 2020 Update. Docker Desktop for Windows will replatform onto <span class="caps">WSL2</span>. <span class="caps">GPU</span> support for <span class="caps">GPU</span> processing (think <span class="caps">CUDA</span>, not intended for games). Linux <span class="caps">GUI</span> app support via wayland.</li>
</ul>
<p>Whew! Even for a short summary, that still ended up being long. It’s been an exciting few days for the .<span class="caps">NET</span> ecosystem, and .<span class="caps">NET</span> 5 promises to keep that excitement alive.</p>
<embed>
<style>
.strike { text-decoration: line-through; }
li { margin: 8px 0 8px 0; }
</style>
</embed></div>
</div>
Native Websockets with Blazor WebAssembly2020-02-01T00:00:00+07:002020-02-01T00:00:00+07:00Will Fuquatag:fuqua.io,2020-02-01:/blog/2020/02/native-websockets-with-blazor-webassembly/<p>A couple of days ago, Blazor WebAssembly 3.2.0 Preview 1 was released (<a class="reference external" href="https://devblogs.microsoft.com/aspnet/blazor-webassembly-3-2-0-preview-1-release-now-available/">announcement</a>). I’m personally excited about this release
because it’s the first Blazor release that contains native support for client-side websockets!</p>
<p>Previously, if you wanted to use websockets, you either had to write your own …</p><p>A couple of days ago, Blazor WebAssembly 3.2.0 Preview 1 was released (<a class="reference external" href="https://devblogs.microsoft.com/aspnet/blazor-webassembly-3-2-0-preview-1-release-now-available/">announcement</a>). I’m personally excited about this release
because it’s the first Blazor release that contains native support for client-side websockets!</p>
<p>Previously, if you wanted to use websockets, you either had to write your own wrapper, or use a larger library like
SignalR that did the wrapping for you. However, if you just wanted to use the normal <tt class="docutils literal">System.Net.WebSockets.ClientWebSocket</tt> class that’s built into .<span class="caps">NET</span>, you could not.</p>
<p>The Mono/<span class="caps">WASM</span> project has actually supported <tt class="docutils literal">ClientWebSocket</tt> for about a year (<a class="reference external" href="https://github.com/mono/mono/pull/12615"><span class="caps">PR</span> 12615</a>). However, some recent changes in Blazor allowed the Blazor project to be able to consume them (<a class="reference external" href="https://github.com/dotnet/aspnetcore/issues/10489"><span class="caps">PR</span> 10489</a>).
The ClientWebSocket implementation is ultimately just <a class="reference external" href="https://github.com/mono/mono/blob/a2d1aec5d2c01483738dfa6e69202462bca68e2b/sdks/wasm/framework/src/WebAssembly.Net.WebSockets/ClientWebSocket.cs">wrapping the <span class="caps">JS</span> interop</a> for you, but this greatly simplifies your code and removes third-party libraries from your project.</p>
<p>Here’s an example class that uses a ClientWebSocket in a Blazor chat client of mine, all bundled up neatly into the new <tt class="docutils literal">IAsyncEnumerable</tt> feature of C#8:</p>
<div class="highlight"><pre><span></span><span class="k">public</span><span class="w"> </span><span class="k">class</span><span class="w"> </span><span class="nc">ChatClientConnection</span><span class="w"></span>
<span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="k">private</span><span class="w"> </span><span class="k">readonly</span><span class="w"> </span><span class="n">ClientWebSocket</span><span class="w"> </span><span class="n">websocket</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="k">private</span><span class="w"> </span><span class="k">readonly</span><span class="w"> </span><span class="n">Uri</span><span class="w"> </span><span class="n">websocketUrl</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="k">public</span><span class="w"> </span><span class="nf">ChatClientConnection</span><span class="p">(</span><span class="n">ClientWebSocket</span><span class="w"> </span><span class="n">websocket</span><span class="p">,</span><span class="w"> </span><span class="n">Uri</span><span class="w"> </span><span class="n">websocketUrl</span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="n">websocket</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="n">websocket</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="n">websocketUrl</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="n">websocketUrl</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="c1">/// <summary></span>
<span class="w"> </span><span class="c1">/// Connect to the websocket and begin yielding messages</span>
<span class="w"> </span><span class="c1">/// received from the connection.</span>
<span class="w"> </span><span class="c1">/// </summary></span>
<span class="w"> </span><span class="k">public</span><span class="w"> </span><span class="k">async</span><span class="w"> </span><span class="n">IAsyncEnumerable</span><span class="p"><</span><span class="kt">string</span><span class="p">></span><span class="w"> </span><span class="n">ConnectAsync</span><span class="p">(</span><span class="w"></span>
<span class="w"> </span><span class="na">[EnumeratorCancellation]</span><span class="w"> </span><span class="n">CancellationToken</span><span class="w"> </span><span class="n">cancellationToken</span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="n">websocket</span><span class="p">.</span><span class="n">ConnectAsync</span><span class="p">(</span><span class="n">websocketUrl</span><span class="p">,</span><span class="w"> </span><span class="n">cancellationToken</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="kt">var</span><span class="w"> </span><span class="n">buffer</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">ArraySegment</span><span class="p"><</span><span class="kt">byte</span><span class="p">>(</span><span class="k">new</span><span class="w"> </span><span class="kt">byte</span><span class="p">[</span><span class="m">2048</span><span class="p">]);</span><span class="w"></span>
<span class="w"> </span><span class="k">while</span><span class="w"> </span><span class="p">(!</span><span class="n">cancellationToken</span><span class="p">.</span><span class="n">IsCancellationRequested</span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="n">WebSocketReceiveResult</span><span class="w"> </span><span class="n">result</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="k">using</span><span class="w"> </span><span class="nn">var</span><span class="w"> </span><span class="n">ms</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">MemoryStream</span><span class="p">();</span><span class="w"></span>
<span class="w"> </span><span class="k">do</span><span class="w"></span>
<span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="n">websocket</span><span class="p">.</span><span class="n">ReceiveAsync</span><span class="p">(</span><span class="n">buffer</span><span class="p">,</span><span class="w"> </span><span class="n">cancellationToken</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="n">ms</span><span class="p">.</span><span class="n">Write</span><span class="p">(</span><span class="n">buffer</span><span class="p">.</span><span class="n">Array</span><span class="p">,</span><span class="w"> </span><span class="n">buffer</span><span class="p">.</span><span class="n">Offset</span><span class="p">,</span><span class="w"> </span><span class="n">result</span><span class="p">.</span><span class="n">Count</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">while</span><span class="w"> </span><span class="p">(!</span><span class="n">result</span><span class="p">.</span><span class="n">EndOfMessage</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="n">ms</span><span class="p">.</span><span class="n">Seek</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="n">SeekOrigin</span><span class="p">.</span><span class="n">Begin</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="k">yield</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">Encoding</span><span class="p">.</span><span class="n">UTF8</span><span class="p">.</span><span class="n">GetString</span><span class="p">(</span><span class="n">ms</span><span class="p">.</span><span class="n">ToArray</span><span class="p">());</span><span class="w"></span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">result</span><span class="p">.</span><span class="n">MessageType</span><span class="w"> </span><span class="p">==</span><span class="w"> </span><span class="n">WebSocketMessageType</span><span class="p">.</span><span class="n">Close</span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="k">break</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="c1">/// <summary></span>
<span class="w"> </span><span class="c1">/// Send a message on the websocket.</span>
<span class="w"> </span><span class="c1">/// This method assumes you've already connected via ConnectAsync</span>
<span class="w"> </span><span class="c1">/// </summary></span>
<span class="w"> </span><span class="k">public</span><span class="w"> </span><span class="n">Task</span><span class="w"> </span><span class="nf">SendStringAsync</span><span class="p">(</span><span class="kt">string</span><span class="w"> </span><span class="n">data</span><span class="p">,</span><span class="w"> </span><span class="n">CancellationToken</span><span class="w"> </span><span class="n">cancellation</span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="kt">var</span><span class="w"> </span><span class="n">encoded</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="n">Encoding</span><span class="p">.</span><span class="n">UTF8</span><span class="p">.</span><span class="n">GetBytes</span><span class="p">(</span><span class="n">data</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="kt">var</span><span class="w"> </span><span class="n">buffer</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">ArraySegment</span><span class="p"><</span><span class="kt">byte</span><span class="p">>(</span><span class="n">encoded</span><span class="p">,</span><span class="w"> </span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="n">encoded</span><span class="p">.</span><span class="n">Length</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">websocket</span><span class="p">.</span><span class="n">SendAsync</span><span class="p">(</span><span class="n">buffer</span><span class="p">,</span><span class="w"> </span><span class="n">WebSocketMessageType</span><span class="p">.</span><span class="n">Text</span><span class="p">,</span><span class="w"> </span><span class="n">endOfMessage</span><span class="p">:</span><span class="w"> </span><span class="k">true</span><span class="p">,</span><span class="w"> </span><span class="n">cancellation</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</pre></div>
<p>What I love about this is it’s just normal .<span class="caps">NET</span> code – no third-party libraries at all, and it just works flawlessly when compiled to WebAssembly. Happy WASMing!</p>
.NET Conf Thailand 20192019-11-02T00:00:00+07:002019-11-02T00:00:00+07:00Will Fuquatag:fuqua.io,2019-11-02:/blog/2019/11/net-conf-thailand-2019/<p>.<span class="caps">NET</span> Conf Thailand 2019 was a huge success, thanks again to <a class="reference external" href="https://twitter.com/kidchenko">Jose Barbosa</a> and <a class="reference external" href="https://www.facebook.com/codesanookpage">Theeranit (Aaron) Pongtongmuang</a>.</p>
<p>I was invited to speak, and I covered the new features in C# 8.0. There are a ton!</p>
<ol class="arabic simple">
<li>Nullable Reference Types</li>
<li>Async Enumerables</li>
<li>Recursive Patterns</li>
<li>Indices and Ranges</li>
<li>Default Interface Members</li>
<li>Static …</li></ol><p>.<span class="caps">NET</span> Conf Thailand 2019 was a huge success, thanks again to <a class="reference external" href="https://twitter.com/kidchenko">Jose Barbosa</a> and <a class="reference external" href="https://www.facebook.com/codesanookpage">Theeranit (Aaron) Pongtongmuang</a>.</p>
<p>I was invited to speak, and I covered the new features in C# 8.0. There are a ton!</p>
<ol class="arabic simple">
<li>Nullable Reference Types</li>
<li>Async Enumerables</li>
<li>Recursive Patterns</li>
<li>Indices and Ranges</li>
<li>Default Interface Members</li>
<li>Static Local Functions</li>
<li>Null Coallescing Assignment</li>
<li>Readonly Members</li>
<li>Using Statements</li>
</ol>
<p>I have my presentation slides and code available <a class="reference external" href="https://github.com/waf/DotNetConfThailand.2019.CSharp8">on GitHub</a>.</p>
<p>You can see my talk on <a class="reference external" href="https://www.facebook.com/dotnetconf/videos/487578191836482/">the dotnetconf page</a>, however the audio volume is too soft to hear. Oh well! You can get
similar content by watching Mads Torgersen’s talk (<a class="reference external" href="https://channel9.msdn.com/Events/dotnetConf/NET-Conf-2019/Whats-new-in-C-8-Part-1">Part 1</a>) and Bill Wagner’s talk (<a class="reference external" href="https://channel9.msdn.com/Events/dotnetConf/NET-Conf-2019/B103">Part 2</a>).</p>
Troubleshooting Assembly Binding Issues in .NET2019-03-16T00:00:00+07:002019-03-16T00:00:00+07:00Will Fuquatag:fuqua.io,2019-03-16:/blog/2019/03/troubleshooting-assembly-binding-issues-in-net/<p>If you’ve developed .<span class="caps">NET</span> for any length of time, chances are you’ve run into a gnarly error like this:</p>
<p><em>System.<span class="caps">IO</span>.FileLoadException: Could not load file or assembly ‘AcmeCorp.Foobar.Utilities, Version=1.2.0, Culture=neutral, PublicKeyToken=367d582291c765f7’ or one of its dependencies.
The located assembly’s …</em></p><p>If you’ve developed .<span class="caps">NET</span> for any length of time, chances are you’ve run into a gnarly error like this:</p>
<p><em>System.<span class="caps">IO</span>.FileLoadException: Could not load file or assembly ‘AcmeCorp.Foobar.Utilities, Version=1.2.0, Culture=neutral, PublicKeyToken=367d582291c765f7’ or one of its dependencies.
The located assembly’s manifest definition does not match the assembly reference.</em></p>
<p>It’s a pretty puzzling error. It means that it <strong>found</strong> version 1.2.0 of a <span class="caps">DLL</span>, but did not use it because a different version was requested (e.g. 1.3.0).</p>
<p>There are a couple of gotchas when troubleshooting these types of errors.</p>
<div class="section" id="ensure-you-don-t-have-any-version-mismatches">
<h2>Ensure you don’t have any version mismatches</h2>
<p>As a first step, ensure that all projects in your solution reference the same version of the problematic <span class="caps">DLL</span> or NuGet package. This is by far the easiest fix. For NuGet packages in Visual Studio, you can right-click your Solution, choose “Manage NuGet Packages for Solution” and use the “Consolidate” tab to fix the issue.</p>
</div>
<div class="section" id="use-fusion-logs-to-better-understand-the-problem">
<h2>Use Fusion Logs to better understand the problem</h2>
<p>.<span class="caps">NET</span> will log more details about the problem to the “Fusion Logs” system. By default, it’s disabled. Enable it by opening your “Developer Command Prompt for Visual Studio” from the start menu <strong>as administrator</strong>. Type the command <tt class="docutils literal">fuslogvw</tt> to start the “Fusion Log Viewer.”</p>
<img alt="" class="align-center" src="/img/fuslogvw.png" style="width: 80%;" />
<p>Click the settings button, choose “Log bind failures to disk” and then click <span class="caps">OK</span>. Start your application again and you should see more details about the bind failures show up in the Fusion Log Viewer.</p>
<p>If you don’t see any log entries show up, there are a few things you can try:</p>
<ul class="simple">
<li>First try restarting your application and, if you’re developing a web application, restarting your App Pool.</li>
<li>The Fusion Log Viewer surprisingly uses the Internet Explorer cache, so you can try clearing the cache by going to “Internet Options → Browsing History → Delete” and deleting the Temporary Internet Files option. Weird but true!</li>
<li>If you’re using <span class="caps">ASP</span>.<span class="caps">NET</span>, you’ll see additional error information on your <span class="caps">ASP</span>.<span class="caps">NET</span> error page simply by having Fusion Logs enabled.</li>
</ul>
<p>Don’t forget to disable Fusion Logs when you’re done! There’s a performance impact for having it enabled.</p>
</div>
<div class="section" id="create-an-assembly-binding-redirect">
<h2>Create an Assembly Binding Redirect</h2>
<p>Finally, you can add an <a class="reference external" href="https://docs.microsoft.com/en-us/dotnet/framework/configure-apps/redirect-assembly-versions">Assembly Binding Redirect</a> to your App.config or Web.config of your entry project by using the <a class="reference external" href="https://docs.microsoft.com/en-us/dotnet/framework/configure-apps/file-schema/runtime/bindingredirect-element">bindingRedirect</a> element. This only works if there are no breaking changes between the two different versions of the <span class="caps">DLL</span>.</p>
<div class="highlight"><pre><span></span><span class="cm"><!-- the following should be placed under the hierarchy <configuration><runtime><assemblyBinding> --></span>
<span class="nt"><dependentAssembly></span>
<span class="nt"><assemblyIdentity</span> <span class="na">name=</span><span class="s">"AcmeCorp.Foobar.Utilities"</span> <span class="na">publicKeyToken=</span><span class="s">"367d582291c765f7"</span> <span class="na">culture=</span><span class="s">"neutral"</span> <span class="nt">/></span>
<span class="nt"><bindingRedirect</span> <span class="na">oldVersion=</span><span class="s">"0.0.0.0-1.3.0.0"</span> <span class="na">newVersion=</span><span class="s">"1.3.0.0"</span> <span class="nt">/></span>
<span class="nt"></dependentAssembly></span>
</pre></div>
<p>The above configuration states “If you find an AcmeCorp.Foobar.Utilities.dll with a version between 0.0.0.0 and 1.3.0.0, redirect it to 1.3.0.0.”</p>
<p><strong>publicKeyToken</strong> can be retrieved using the <tt class="docutils literal">sn</tt> utility. From your Developer Command Prompt, <tt class="docutils literal">cd</tt> to the location of your <span class="caps">DLL</span>, and run <tt class="docutils literal">sn <span class="pre">-T</span> YourDll.dll</tt>. It will display the public key token.</p>
<p><strong>oldVersion / newVersion</strong> can be confusing. There are many different types of versions that a <span class="caps">DLL</span> can have. What we care about is version in the assembly manifest. From your Developer Command Prompt, <tt class="docutils literal">cd</tt> to the location of your <span class="caps">DLL</span> and run <tt class="docutils literal">ildasm YourDll.dll</tt>. <span class="caps">ILDASM</span> will open your assembly. Click on the <tt class="docutils literal"><span class="caps">MANIFEST</span></tt> node, and you’ll see a version like <tt class="docutils literal">.ver 1:3:0:0</tt> which would correspond to version <tt class="docutils literal">1.3.0.0</tt> in your App/Web.config.</p>
<p>These are all the tricks I’ve learned to troubleshooting various assembly binding issues. Ideally you can have consistent versions of each <span class="caps">DLL</span> in your application, but hey, the world is a complex place!</p>
</div>
A Caching HTTP Proxy using Titanium Web Proxy2019-01-19T00:00:00+07:002019-01-19T00:00:00+07:00Will Fuquatag:fuqua.io,2019-01-19:/blog/2019/01/a-caching-http-proxy-using-titanium-web-proxy/<p>At my work at Jetabroad I do a lot of integration with third-party webservices (like everyone these days).
The webservices, especially the test endpoints, are of variable stability and responsiveness.</p>
<p>The responsiveness issue can be maddening when you’re trying to iterate quickly. I prefer doing
the bulk of my …</p><p>At my work at Jetabroad I do a lot of integration with third-party webservices (like everyone these days).
The webservices, especially the test endpoints, are of variable stability and responsiveness.</p>
<p>The responsiveness issue can be maddening when you’re trying to iterate quickly. I prefer doing
the bulk of my development via unit tests or integration tests to isolate myself as much as possible.
However, I still ultimately find myself developing directly against these third-party services from
time to time.</p>
<p>I built <a class="reference external" href="https://github.com/waf/catchy">Catchy</a> to help solve this pain. When you start it, you provide a whitelist of domains to
intercept. Catchy will examine your outbound <span class="caps">REST</span> or <span class="caps">SOAP</span> requests to those domains, and then
cache the inbound response based on the hash of the outbound request.</p>
<img alt="" class="align-center" src="https://raw.githubusercontent.com/waf/catchy/master/demo.gif" style="width: 80%;" />
<div class="section" id="titanium-web-proxy-an-amazing-library">
<h2>Titanium Web Proxy – an amazing library</h2>
<p>Early prototypes were originally built on top of Fiddler Core, but after Fiddler Core was killed by
Telerik, I transitioned it over to the excellent <a class="reference external" href="https://github.com/justcoding121/Titanium-Web-Proxy">Titanium Web Proxy</a> project. It makes intercepting
and analyzing requests, even over <span class="caps">TLS</span>, very straightforward.</p>
<p>The proxy code itself is straightforward. The following snippet of Titanium Web Proxy code allows
you to intercept <span class="caps">HTTP</span> and <span class="caps">HTTPS</span> requests (via <span class="caps">HTTP</span> 1.1 and <span class="caps">HTTP2</span>!) and run arbitrary C# functions
to inspect / modify the requests and responses:</p>
<div class="highlight"><pre><span></span><span class="kt">var</span><span class="w"> </span><span class="n">proxyServer</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">ProxyServer</span><span class="p">();</span><span class="w"></span>
<span class="kt">var</span><span class="w"> </span><span class="n">explicitEndPoint</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">ExplicitProxyEndPoint</span><span class="p">(</span><span class="n">ipAddress</span><span class="p">,</span><span class="w"> </span><span class="n">port</span><span class="p">,</span><span class="w"> </span><span class="k">true</span><span class="p">);</span><span class="w"></span>
<span class="n">proxyServer</span><span class="p">.</span><span class="n">EnableHttp2</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="k">true</span><span class="p">;</span><span class="w"></span>
<span class="n">proxyServer</span><span class="p">.</span><span class="n">CertificateManager</span><span class="p">.</span><span class="n">CreateRootCertificate</span><span class="p">(</span><span class="k">false</span><span class="p">);</span><span class="w"></span>
<span class="n">proxyServer</span><span class="p">.</span><span class="n">CertificateManager</span><span class="p">.</span><span class="n">TrustRootCertificate</span><span class="p">();</span><span class="w"></span>
<span class="n">proxyServer</span><span class="p">.</span><span class="n">AddEndPoint</span><span class="p">(</span><span class="n">explicitEndPoint</span><span class="p">);</span><span class="w"></span>
<span class="c1">// specify your callbacks here</span>
<span class="n">explicitEndPoint</span><span class="p">.</span><span class="n">BeforeTunnelConnectRequest</span><span class="w"> </span><span class="p">+=</span><span class="w"> </span><span class="n">BeforeTunnelConnectRequest</span><span class="p">;</span><span class="w"></span>
<span class="n">proxyServer</span><span class="p">.</span><span class="n">BeforeRequest</span><span class="w"> </span><span class="p">+=</span><span class="w"> </span><span class="n">OnRequestHandler</span><span class="p">;</span><span class="w"></span>
<span class="n">proxyServer</span><span class="p">.</span><span class="n">BeforeResponse</span><span class="w"> </span><span class="p">+=</span><span class="w"> </span><span class="n">OnResponseHandler</span><span class="p">;</span><span class="w"></span>
<span class="n">proxyServer</span><span class="p">.</span><span class="n">ExceptionFunc</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="n">OnErrorFunc</span><span class="p">;</span><span class="w"></span>
<span class="n">proxyServer</span><span class="p">.</span><span class="n">Start</span><span class="p">();</span><span class="w"></span>
<span class="n">proxyServer</span><span class="p">.</span><span class="n">SetAsSystemProxy</span><span class="p">(</span><span class="n">explicitEndPoint</span><span class="p">,</span><span class="w"> </span><span class="n">ProxyProtocolType</span><span class="p">.</span><span class="n">AllHttp</span><span class="p">);</span><span class="w"></span>
</pre></div>
<p>It’s rare to find a network library that works completely as advertised, and exposes a complex
concept behind an easy to understand <span class="caps">API</span>. Well done to them!</p>
</div>