Blazor WebAssembly, Monaco and Antlr – Building the AutoStep Editor as a Blazor App

I’m writing this post to show people the possibilities of WebAssembly and Blazor, using an open-source project I’m working on right now.

In this post we’ll cover:

  • Integrating the Monaco Code Editor with Blazor (and Razor Component Libraries in general)
  • Blazor to TypeScript Interop Tips
  • Manual Tokenisation of Code in Monaco (by a .NET Assembly!), including a quick look at performance.
  • Feeding Compilation Results from .NET to Monaco.

With the tools available to me, I can do real-time syntax highlighting and compilation of AutoStep tests in-browser, using WebAssembly to run my .NET library that does a lot of the heavy lifting, and the Monaco editor to provide the actual text editor behaviour. You can do some really cool stuff when you combine the power of .NET with a web-based user interface.

You can find all the code for the AutoStep Editor I’m going to be referencing in the GitHub repository, https://github.com/autostep/AutoStep.Editor.

Before we dive in, there’s a bit of background to cover.

Background

To give a little context, I’m currently building the AutoStep Toolkit.

AutoStep is a new compiler, linker and runner for BDD (Business Driven Development) tests, based on Gherkin syntax, but with some extra language features.

You can find the core library that provides this functionality at https://github.com/autostep/AutoStep.

I need to build a User Interface for writing AutoStep tests that is targeted at non-developer users, so using Visual Studio or VS Code as an editor doesn’t give the user experience I want.

I’ve chosen Blazor because:

  • I can load my netstandard AutoStep package directly into WebAssembly, so I don’t need a server component to run compilation.
  • I prefer to keep the amount of Javascript I have to write to a minimum.
  • I can share types between the front-end and the AutoStep project system.

Right now, I’m just building the basic editor control, before we build the rest of the user interface around it.

Below you can see a little demo GIF of how the editor control looks right now. You can see real-time syntax highlighting and test compilation as you type, with syntax errors being presented by the editor.

The rest of this post is basically going to go over how it works, and some of the WebAssembly magic that gives us this behaviour.

Integrating Monaco

Monaco is the VS Code editor, released as a standalone package that anyone can use; it’s really powerful, and gives us loads of basic text editor behaviour out of the box, even before we add the syntax highlighting and IDE-type functionality.

The first task was to get Monaco working as a Blazor component. I knew that I would need at least some Javascript code to function as the Interop layer, so rather than put that code in my main Blazor Client project (AutoStep.Editor.Client), I decided to put all the Monaco behaviour in a new Razor Component Library (AutoStep.Monaco), which I can use from my main project.

That way, I can keep the node_modules out of my main application project, nice and self-contained in it’s own folder.

I feel like it’s a pretty pleasing pattern to keep any JS interop out of the main Blazor app, in separated components. That way, the main application stays clean and only has to worry about components and general app state.

It also makes each component easier to test on its own.

I’m going to use TypeScript for my interop code, partly because I just like being in a typed world, but also because I can then consume the type definitions exposed by Monaco.

The only actual npm package I need to install and redistribute is monaco-editor, but I also need Webpack to compile my TypeScript and bundle everything together, plus the various Webpack plugins.

You can check the full package.json file for the set of required packages. There’s only 10 packages listed, but even these dependencies result in 5483 installed packages!

To configure Webpack correctly, I used the Monaco Webpack Plugin, which just simplifies getting Monaco building under Webpack. If you follow the instructions in their README, you can’t really go wrong.

Static Files in Razor Component Libraries

One nice feature of Blazor is that if you put your static files in the wwwroot folder of a Razor Component project, when you reference your Component project from your main Blazor App project, you can reference those static resources in your HTML, just by using the special _content path:

<!-- Use the name of the referenced project (or package) -->
<script src="_content/AutoStep.Monaco/app.bundle.js"></script>

For this to work with the Webpack build, I had to do two things:

  • Configure Webpack to output my bundles to the wwwroot folder
  • Configure Monaco to load its dependencies from the _content/AutoStep.Monaco path

The first part was straight-forward, you just have to change the Webpack output path:

//...
output: {
  globalObject: "self",
  filename: "[name].bundle.js",
  path: path.resolve(__dirname, 'wwwroot')
},
//...

For the Monaco configuration, the _content path has to be configured in three different locations. The first two are in the Webpack configuration file:

module: {
    rules: [
        // Other rules here...
        {
            test: /\.ttf$/,
            loader: 'file-loader',
            options:
            {
                publicPath: "/_content/AutoStep.Monaco"
            }
        }]
},
plugins: [
    new MonacoWebpackPlugin({publicPath: '/_content/AutoStep.Monaco/', languages: []})
]

I’ve also told the MonacoWebpackPlugin to not include any built-in languages in the output, because I’m not going to need them.

Finally, in the ‘entry point’ of your Javascript/Typescript (my MonacoInterop.ts), you need to tell Monaco where to load its web workers from:

// @ts-ignore
self.MonacoEnvironment = {
    getWorkerUrl: function (moduleId, label) {
        return "./_content/Autostep.Monaco/editor.worker.bundle.js";
    }
};

Once all the above is done, I can just include the app bundle in my Blazor Client index.html file, and it will load in all the Monaco dependencies:

<body>
    <app class="d-flex">Loading...</app>

    <div id="blazor-error-ui">
        An unhandled error has occurred.
        <a href="" class="reload">Reload</a>
        <a class="dismiss">đź—™</a>
    </div>
    <script src="_content/AutoStep.Monaco/app.bundle.js"></script>
    <script src="_content/Blazor.Fluxor/index.js"></script>
    <script src="_framework/blazor.webassembly.js"></script>
</body>

Blazor JS Interop & TypeScript

Once I’ve got the Monaco code loading in, I now need to use it. I’ll just go over a few tips for using TypeScript for doing Blazor JS Interop.

Interop Classes

The first tip is to define a sensible boundary between your .NET code and your TypeScript. First up, let’s define an entry-point TypeScript class attached to ‘window’:

class MyInterop 
{
    doSomething() 
    {
    }

    getSomething() : string
    {
    }
}

window['MyInterop'] = new MyInterop();

In your C# code, create an internal class of the same name, and encapsulate those methods (I’ve also defined wrappers for the IJSRuntime methods that automatically prefix the name of my TypeScript class):

internal class MyInterop
{
    private readonly IJSRuntime jsInterop;
    private readonly ILogger logger;

    private const string InteropPrefix = "MyInterop.";

    public MyInterop(IJSRuntime runtime, ILogger<MyInterop> logger)
    {
        this.jsInterop = jsInterop;
        this.logger = logger;
    }

    public async ValueTask DoSomething()
    {
        await InvokeVoidAsync("doSomething");
    }

    public async ValueTask<string> GetSomething()
    {
        return await InvokeAsync<string>("getSomething");
    }

    private ValueTask<TResult> InvokeAsync<TResult>(string methodName, params object[] args)
    {
        var fullname = InteropPrefix + methodName;
        logger.LogTrace("InvokeAsync: {0}", fullname);
        return jsRuntime.InvokeAsync<TResult>(fullname, args);
    }

    private ValueTask InvokeVoidAsync(string methodName, params object[] args)
    {
        var fullname = InteropPrefix + methodName;
        logger.LogTrace("InvokeVoidAsync: {0}", fullname);
        return jsRuntime.InvokeVoidAsync(fullname, args);
    }
}

Log your JS Interop calls! This will help a lot with debugging later.

In my AutoStep.Monaco library, I’ve got precisely this set-up (albeit with more methods), with the TypeScript in MonacoInterop.ts, and the C# in MonacoInterop.cs.

I added an extension method to my Razor Component Library that adds my MonacoInterop class to the Service Collection; I can call this during startup in my Blazor App.

public static class ServiceCollectionExtensions
{
    /// <summary>
    /// Add services for the Monaco component.
    /// </summary>
    public static IServiceCollection AddMonaco(this IServiceCollection services)
    {
        services.AddSingleton<MonacoInterop>();
        return services;
    }
}

Then I can inject the MonacoInterop class into any of my Razor Components inside my AutoStep.Monaco project, and invoke my TypeScript methods that way.

Calling Back into .NET Code from TypeScript

When an ‘event’ of some form happens inside the Monaco Editor, I need to invoke a method in my .NET Code.

So far, I’ve found the following pattern to be pretty useful.

First up, add a method to your Interop class to register an event handler.

public async ValueTask RegisterLanguageTokenizer(string languageId, string extension, ILanguageTokenizer tokenizer)
{
    // Wrap the 'tokenizer' in a DotNetObjectReference.
    await InvokeVoidAsync("registerLanguageTokenizer", languageId, extension, DotNetObjectReference.Create(tokenizer));
}

The DotNetObjectReference passes the object to JS in a way that tracks the original object.

In the implementation of ILanguageTokenizer, I have a couple of methods, all marked as [JSInvokable], which indicates they can be called from Javascript.

In your TypeScript Interop class, add the registerLanguageTokenizer method:

registerLanguageTokenizer(languageId: string, extension: string, blazorCallback: IBlazorInteropObject)
{
  // Store the blazorCallback object somewhere to call it in an event handler.
}

The IBlazorInteropObject is something I’ve added; it’s a simple TypeScript interface that defines the useful methods available on the object wrapper Blazor actually passes as that parameter.

/**
 * Interface that defines the useful methods on the .NET object reference passed by Blazor.
 */
export interface IBlazorInteropObject {
    invokeMethodAsync<T>(methodName: string, ...args: any[]): Promise<T>;
    invokeMethod<T>(methodName: string, ...args: any[]): T;

}

I can then use this IBlazorInteropObject to invoke my .NET code.

export class AutoStepTokenProvider implements languages.TokensProvider {
    private callback: IBlazorInteropObject;

    constructor(blazorCallback: IBlazorInteropObject) {
        this.callback = blazorCallback;
    }

    getInitialState(): languages.IState {
        return new AutoStepTokenState(this.callback.invokeMethod<number>("GetInitialState"));
    }

    tokenize(line: string, state: languages.IState): languages.ILineTokens {

        if (state instanceof AutoStepTokenState)
        {
            var result: any = this.callback.invokeMethod("Tokenize", line, state.tokenState);

            return { tokens: result.tokens, endState: new AutoStepTokenState(result.endState) };
        }

        throw "Invalid start state";
    }
}

Line Tokenisation & Syntax Highlighting

For people unfamiliar with it, syntax highlighting code usually involves tokenising a given line of code, which uses a lexer to go through a block of text and produce a set of tokens that give the position of named language constructs, like keywords, variables, strings, etc. The editor then knows which colours to apply to different parts of a line of text.

Monaco allows you to define a ‘grammar’ for a language you want to apply syntax highlighting to., using their Monarch system for describing languages using JSON. Monaco then does the tokenising for you, based on that configuration.

The problem with using Monarch in my situation is that the tokenisation would not be context-sensitive. By that, I mean that the tokenisation can only work off the content of the file it is highlighting, and cannot base the set of returned tokens on anything else.

In my situation, I want to highlight the Given/When/Then lines of a test a different colour if there is no backing step to call; in addition, I only know which part of a step is an argument (in red) based on which step it binds against.

This contextual information cannot be obtained just through using a declarative grammar; I need a more manual approach.

Luckily, Monaco lets you define a manual token provider, using the setTokensProvider method. By implementing the Monaco-defined interface languages.TokensProvider, we can run our own custom code when Monaco needs to re-tokenise a line.

I showed you the TypeScript implementation of that interface earlier, when we were looking at how to call a .NET object from Javascript. All that the AutoStepTokenProvider TypeScript class does is call into an object in our Blazor .NET code, the AutoStepTokenizer, to handle the actual tokenisation.

The JS call for tokenisation must be a synchronous call because the Monaco tokenisation methods don’t allow me to return a promise (although it does execute in a background web worker).

Typically you’d want to make asynchronous calls into your .NET code where possible, but we can’t do that here.

To achieve the required tokenisation performance, I added Line Tokenisation support in the core AutoStep library, which is effectively a special-cased fast path through the normal compilation and linking process.

[JSInvokable]
public TokenizeResult Tokenize(string line, int state)
{
    try
    {
        var castState = (LineTokeniserState)state;

        logger.LogTrace("Tokenise Start in State {0}: {1}", castState, line);

        // Use the project compiler (in the core library) to tokenise.
        var tokenised = projectCompiler.TokeniseLine(line, castState);
        
        // Create the set of models that Monaco expects
        var tokenArray = tokenised.Tokens.Select(x => 
            new LanguageToken(x.StartPosition, TokenScopes.GetScopeText(x.Category, x.SubCategory)));

        return new TokenizeResult((int)tokenised.EndState, tokenArray);
    }
    catch (Exception ex)
    {
        logger.LogError(ex, "Tokenisation Error");
    }

    return new TokenizeResult(0, Array.Empty<LanguageToken>());
}

Once the AutoStep Core library returns the set of tokens for a line, I need to convert those tokens into TextMate scopes. Scopes are effectively names for the different tokens you can get, and Monaco can style each scope differently.

I put the scope mapping configuration in a static array in a TokenScopes class:

static TokenScopes()
{
    // Set up our scopes.
    InitScope("comment.line.number-sign", LineTokenCategory.Comment);
    InitScope("keyword", LineTokenCategory.StepTypeKeyword);
    InitScope("keyword", LineTokenCategory.EntryMarker);
    InitScope("entity.name", LineTokenCategory.EntityName);
    InitScope("entity.name.section", LineTokenCategory.EntityName, LineTokenSubCategory.Scenario);
    InitScope("entity.name.section", LineTokenCategory.EntityName, LineTokenSubCategory.ScenarioOutline);
    InitScope("entity.name.type", LineTokenCategory.EntityName, LineTokenSubCategory.Feature);
    InitScope("entity.annotation", LineTokenCategory.Annotation);
    InitScope("entity.annotation.opt", LineTokenCategory.Annotation, LineTokenSubCategory.Option);
    InitScope("entity.annotation.tag", LineTokenCategory.Annotation, LineTokenSubCategory.Tag);
    InitScope("string", LineTokenCategory.BoundArgument);
    InitScope("string.variable", LineTokenCategory.BoundArgument, LineTokenSubCategory.ArgumentVariable);
    InitScope("variable", LineTokenCategory.Variable);
    InitScope("markup.italic", LineTokenCategory.Text, LineTokenSubCategory.Description);
    InitScope("text", LineTokenCategory.Text);
    InitScope("entity.step.text", LineTokenCategory.StepText);
    InitScope("entity.step.text.bound", LineTokenCategory.StepText, LineTokenSubCategory.Bound);
    InitScope("entity.step.text.unbound", LineTokenCategory.StepText, LineTokenSubCategory.Unbound);
    InitScope("table.separator", LineTokenCategory.TableBorder);
}

Finally, I define my own theme for Monaco so I can style the scopes:

editor.defineTheme('autostep', {
    base: 'vs',
    inherit: true,
    rules: [
        { token: "markup.italic", fontStyle: 'italic' },
        { token: "string.variable", fontStyle: 'italic' },
        { token: "variable", fontStyle: 'italic' },
        { token: "entity.step.text.unbound", foreground: '#969696' },
        { token: "entity.annotation.opt", foreground: '#fbad38' },
        { token: "entity.annotation.tag", foreground: '#fbad38' }
    ],
    colors: {} 
});

editor.setTheme('autostep');

Performance

It’s important to measure performance of code like this, especially because it needs to update the display in real-time as the user types.

If you run the profiler in Chrome DevTools, you can see the activity happening on the background thread that calls into the WebAssembly system, and get an idea of how long your code is spending in the .NET world.

I’ve highlighted which bits are doing what in the call stack, along with some timings.

It’s pretty quick! Even considering the hops into the WebAssembly space and back, tokenisation generally ranges between 3 and 6ms.

A lot of that performance, though, is down to the awesome parser engine we use in the AutoStep Core library, Antlr.

Antlr Overview

Antlr is a parser generator. It can take a grammar describing your language, and output a parser that will turn a block of text into a structured parse tree.

Considering the complexity of the task it has to perform, it produces really efficient parsers.

The Antlr generator is written in Java, but there are runtimes for the parser for a number of platforms, including .NET.

I’m not going to go into loads of depth on how Antlr works, because it is a really broad topic, but I can strongly recommend the excellent book by Terrence Parr, which is a great intro and reference for Antlr.

The full lexer grammar and parser grammar for the AutoStep language can be found in the AutoStep repo.

Line Tokenising Parser

The full parse tree for AutoStep works over the entire file, validating positions and order in a detailed way. That won’t work for tokenising a single line at a time (and risks being too slow), so I added a simpler line-by-line entry-point into the parser (AutoStepLineTokeniser) that helps me tokenise just for this syntax highlighting purpose:

You might ask, why do I even need a parser for this? Surely a lexer is all I need to generate the tokens?

I generate a parse tree for each line because:

  • I want the parser to tell me what ‘alternative’ of the possible line structures I’m looking at.
  • The set of tokens that I report for syntax highlighting are based on similar structures to the full compile, which expect at least a partial parse tree.

Once I have the Antlr parse tree for the single line I can built a set of line tokens with the appropriate categorisations for each token.

If the line is a Step Reference (Given/When/Then), I ask the AutoStep linker if the Step Reference can be bound to an existing step.

The high-level pseudo-code for this whole process looks a little like this:

var parseTree = GetAntlrParseTree(lineText);

if(parseTree is StepReference stepRef)
{
    if(linker.TryBindStep(stepRef))
    {
        return GetTokensForBoundStep(stepRef);
    }

    return GetTokensForUnboundStep(stepRef);
}

return GetRegularTokens(parseTree);

Once the tokens are handed back to the Blazor App, they get turned into scopes and handed off to Monaco for rendering.

Compilation, Linking, and Message Markers

Ok, so we’ve got line tokenisation, and syntax highlighting. Now I want to show underline markers when something is wrong.

Monaco makes this an absolute breeze, with a concept called ‘markers’, which are for precisely this purpose, but let’s take a look at how this is arranged. First, let’s look at the line in the Razor file that renders our custom MonacoEditor component:

<MonacoEditor Uri="@currentFile.FileUri.ToString()" 
              Value="@currentFile.Source.OriginalBody" 
              ModelMarkers="currentMarkers" 
              OnModelChanged="m => CodeChangedHandler(m.CurrentValue)" 
              LanguageId="autostep" />

When the content of the Monaco Editor changes, after a short delay (so we don’t recompile after every keystroke), our CodeChangedHandler will be invoked, with the new content of the editor as an argument.

When currentMarkers changes, the MonacoEditor component will pass those new markers down to the Monaco Javascript.

When the code for the file is changed, we ask the Project Compiler to compile and link the entire project. Only those files that have changed actually get compiled.

When that has completed, we have a set of Compilation & Linker Messages for the file, for example:

(8,17,8,25): Error ASC00011: Not expecting an Examples block here; did you mean to define 'My Scenario' as a Scenario Outline rather than a Scenario?
(3,1): Error ASC20002: There are multiple matching step definitions that match this step.

To use those in Monaco, we just need to convert them into MarkerData structures, i.e. the format Monaco understands. I’ve defined a MarkerData class in C# that serialises directly to the equivalent Javascript structure.

private static MarkerData GetMarkerDataFromMessage(CompilerMessage msg)
{
    var severity = msg.Level switch
    {
        CompilerMessageLevel.Error => MarkerSeverity.Error,
        CompilerMessageLevel.Warning => MarkerSeverity.Warning,
        _ => MarkerSeverity.Info
    };

    var endPosition = msg.EndColumn;

    if(endPosition is null)
    {
        endPosition = msg.StartColumn;
    }
    else
    {
        // Expand message end to the location after the token
        endPosition++;
    }

    return new MarkerData($"ASC{(int)msg.Code:D5}", msg.Message, severity, msg.StartColumn, msg.StartLineNo, endPosition.Value, msg.EndLineNo ?? msg.StartLineNo);
}

Once I have the correct data structures, I can just pass those over to my TypeScript class using regular JS Interop, and call editor.setModelMarkers to update the set.

/**
    * Set the model markers for a text model.
    * @param textModelUri The URI of the text model.
    * @param owner The owner of the markers.
    * @param markers The full set of new markers for the model.
    */
setModelMarkers(textModelUri: string, owner: string, markers: editor.IMarkerData[])
{
    var modelCtxt = this.models[textModelUri];

    if (!modelCtxt) {
        throw "Specified model not created.";
    }

    editor.setModelMarkers(modelCtxt.textModel, owner, markers);
}

Hey, presto! Compilation errors, syntax highlighting, all in the browser with no server work beyond static file hosting!

What’s Next

Features going into the AutoStep Editor over the next few months include:

  • An actual User Interface, rather than just an Editor!
  • Intellisense, and automatic step suggestions as you type.
  • Hover documentation, showing step documentation if you hover over one.
  • Go-To-Reference for steps, that navigates to the Step Definition for a step if you defined a step in an AutoStep file.

Keep an eye on the repository if you want to see how it goes, there may well be another couple of follow-up posts as we make progress.

.NET Asynchronous Disposal – Tips for Implementing IAsyncDisposable on your own Types

Background – Why Async Dispose?

The .NET Team recently added support for asynchronous disposal of objects, via a new IAsyncDisposable interface.

A lot of the examples you can find will use this to let you dispose of asynchronous streams, but it’s also useful for disposal of other objects that may potentially trigger I/O.

This allows you to write code like this (which works in .NET Core 3.0, with C# 8):

Notice the await in front of the using statement? That will tell the using block to call DisposeAsync on the SqlConnection object when we exit the using block, instead of the regular Dispose method, and await on the result.

What’s the benefit of this over a typical using? Well, if the database connection needs to go over the network to reset the connection, it will return the thread to the thread pool to do other work, rather than blocking it while the Dispose takes place.

The great thing about .NET Core 3.0 is that for any services you have registered as Scoped (i.e. they only live for the duration of the current HTTP request) that implement IAsyncDisposable will be disposed of asynchronously at the end of the request, giving valuable thread time back to processing actual requests.

I recently added the functionality in the Autofac Dependency Injection library to support calling DisposeAsync on a lifetime scope, which in turn calls DisposeAsync on all services in that scope that implement IAsyncDisposable. This extends to if you use Autofac as your .NET Core Service Provider as well. The functionality will be released in Autofac 5.0.0, or you can check out the relevant GitHub PR now to see what changes went in.

I thought I’d use this blog post to help people write their own classes that implement IAsyncDisposable, since I couldn’t find a lot of documentation on it, and had to go digging into .NET Core code on GitHub to figure out the best approach.

Implement IDisposable As Well

IAsyncDisposable isn’t a replacement for IDisposable, it’s an additional way to dispose.

Basically, if you implement IAsyncDisposable in your class, you should probably implement IDisposable too and perform the synchronous equivalent of your disposal operation.

This goes double for library authors, who are not in control of the code that creates the object that needs to be disposed.

There’s a couple of reasons for this:

  1. If you don’t have a regular Dispose method, code that doesn’t run in an async context will have to block on your DisposeAsync to make it sync, which kind of defies the point, and is unpleasant:
  1. If your class gets put in a container, and then the container is disposed synchronously, an exception will be thrown (this is the behaviour of Autofac and the default .NET Core DI system), because these containers will refuse to call DisposeAsync from inside a regular Dispose:
The exception we get if we don’t dispose asynchronously.

Only Add IAsyncDisposable If You Need To

This one is pretty simple; you should only add IAsyncDisposable to your class if you or a derived class may allocate resources that also implement IAsyncDisposable.

Don’t do this:

SemaphoreSlim doesn’t implement IAsyncDisposable, so all this does is use up another thread pool thread to run the Dispose.

Derived Classes

If you are writing a base class that might have derived classes with resources that need disposing asynchronously, you may wish to introduce a virtual DisposeAsync method if you also have a base Dispose method.

In this case, I would suggest making your default implementation call Dispose directly without awaiting and return synchronously:

Base classes can override the DisposeAsync method if they have resources that can be disposed of asynchronously, otherwise they can just override Dispose.

Only Dispose Once (Sync or Async)

It’s recommended practice to make Dispose re-entrant, and only dispose of its resources once. With the asynchronous disposal behaviour, this is still true, and importantly, you should only allow either Dispose or DisposeAsync to actually do the dispose.

So your classes should have this pattern:

The reason I set isDisposed to true in the above example before awaiting is because setting it afterwards would make it possible for a caller to double-dispose, by not awaiting on DisposeAsync, then calling Dispose. It’s unlikely, but possible.

If the class may be used in a multi-threaded context, consider using Interlocked methods to set isDisposed, to make sure two threads don’t try disposing at the same time.

Targeting netstandard2.0

This bit is mostly targeted at library developers, who might be targeting netstandard versions.

While the language implementations for asynchronous disposal are only available in netstandard2.1, there is a package from Microsoft that provides the IAsyncDisposable interface and related types for netstandard2.0 and .NET 4.6.1, Microsoft.Bcl.AsyncInterfaces (as David Fowler kindly pointed out to me in the Autofac PR).

This allows you to add conditional references that mean your library can implement asynchronous disposal in versions prior to .NET Standard 2.1, by adding the following conditional package reference:

Consider Adding GC.SuppressFinalize to DisposeAsync

If your class has a finalizer (or a derived class may have one), then you may already be calling GC.SuppressFinalize(this) in your Dispose method.

Because your DisposeAsync method is another Dispose method, it should also call GC.SuppressFinalize so the GC doesn’t have to call your destructor later.

This is a more complete example that provides protected virtual methods for disposal, in line with the recommended IDisposable pattern:

Putting GC.SuppressFinalize in the DisposeAsync method will actually cause a violation of the CA1816 analyzer rule if you have the analyzers installed, that says GC.SuppressFinalize should only be called from Dispose. I’m hoping that the rule will get updated at some point, but for now you may need to suppress that rule for the DisposeAsync method.

Wrap-Up

So, IAsyncDisposable can be really handy if you have resources to dispose of that may use I/O in that disposal, but be careful using it, and only add it if you actually need to!

ASP.NET Core 3.0 – Logging in the Startup Class (with NLog)

With ASP.NET Core 3.0, the ability to inject an ILogger or ILoggerFactory into the Startup class has been removed, so we can no longer do this:

I understand why this has been done, because creating a temporary DI container just for the startup process adds a lot of complexity and potential for errors.

However, my ConfigureServices method (and the new ConfigureContainer method added in 3.0) does quite a bit of work, including loading extension assemblies; I want to be able to log during that time.

I also want to make sure I only pass around the ILogger from the Microsoft.Extensions.Logging namespace to other objects used at startup.

The Workaround

I use NLog for my logging (https://nlog-project.org/). In my Program’s Main method I configure it like so:

You can grab details on how to create an nlog.config file from the NLog docs, I won’t go into it here.

Then, in my Startup class, I can create the NLogLoggerProvider class (this is sort of a factory for creating the Microsoft ILogger instances), and from that I can get my logger instance:

Hey presto, logging in the Startup class. Note that we are obviously outside the entire DI system for this logging (which is sort of the point).

Adding the Username to the Logs for every ASP.NET Core Request with NLog

I’m currently investigating how we port a large ASP.NET application to ASP.NET Core, and one of the things I had to figure out this morning was how to include information about the logged-in user in our logs.

The most important value to collect is the username, but I also need to be able to collect other information from the session at some point.

In the original ASP.NET app we did some slightly questionable HttpContext.Current access inside the logging providers, which I didn’t love.

In ASP.NET Core however, I can combine the use of the middleware system with NLog to add this information to my logs in a much better/easier way.

Adding NLog to my App

To add NLog to my ASP.NET Core app, I just followed the basic instructions at https://github.com/NLog/NLog/wiki/Getting-started-with-ASP.NET-Core-2 to get up and going (that guide tells you what you need to put in your Program and Startup classes to wire everything up).

I then updated the default HomeController to write a log message in the index page:

So when I launch my app, I get my log messages out (this is just the basic ASP.NET Core site template):

Adding our Username to Log Events

First up, I’m just going to add an extremely basic action to my HomeController that will sign me in with a test user (I’ve already set up the Startup class configuration to add cookie authentication):

Now we can do the middleware changes (this is the important bit). In the Startup class’ Configure method, we have an additional Use method:

The MappedDiagnosticsLogicalContext class is an NLog class that lets you provide values that are scoped to the current async context. These values are attached to every log event raised inside the using block. The call to next() inside our using means that the entirety of the middleware pipeline (from that point onwards) has the userName property attached to it.

Displaying the Username in the Log

The last part of this is to update our nlog.config to display the username.

To do this, we use the MDLC Layout Renderer to pull the userName property out of the log event, by adding ${mdlc:userName} inside our layout:

Now, if we start the application, and log in, we get our username in each log event!

The real bonus of assigning the username inside the middleware though is that the extra detail gets added to the internal Microsoft.* logs as well:

You’ll notice that not all the log messages have a username value. Why is that?

The reason is that those log messages come from middleware that occurs earlier in the pipeline than our middleware that assigns the username, so those log events won’t contain that property.

In conclusion…

So, you’ve seen here how to use the NLog MappedDiagnosticsLogicalContext class and ASP.NET Core middleware to add extra information to all the log messages for each request. Don’t forget that you can add as much information to the log as you need, for example you could pull some user session state value out of the Session and add that too.

Managing Big Enterprise Applications in the .NET Ecosystem

I’m going to spend a few minutes here discussing some advice for designing/maintaining large enterprise-grade .NET applications, particularly ones that you sell to others, rather than in-house creations.

Disclaimer: I work largely with big applications used by enterprise customers. I imagine a lot of people reading this do as well, but plenty of people may disagree with some of my thoughts/suggestions. What follows is just based on my experience of designing and deploying user-driven ASP.NET applications.

A Brief Defense of (Deployment) Monoliths

Microservices are all the rage right now, and they are very cool, I will not deny that; small blocks of easy-to-maintain logic that all build, deploy and start quickly are brilliant. They’re great when you are deploying your own software, either onto your own premises or in the cloud; but what if your software has to be deployed onto someone else’s environment, by the owner of that environment?

What if they don’t use containers, or even use virtualisation?
What if they have no DevOps pipeline at all, and everything must be done manually?
What if those global customers have disparate regulatory and internal governance concerns that govern how and where data is stored, and how your application is managed?

In these situations, deployment simplicity is one of the most important considerations we have, and microservices deployment is by no means simple.

What I need is to keep the number of deployable components to a minimum. My goal is a one-click installer, followed by minimal configuration.

I asked a panel at a recent Microsoft Azure conference what solutions/plans they had for taking a complex microservices architecture and deploying it in someone else’s infrastructure as a simple-to-install component. If they use Azure as well, then you might be in luck in the near future, but other than that I didn’t get any answers that gave me hope for distributable microservice packages.

Managing Monoliths

In the modern development ecosystem, some people think ‘monolith’ is a dirty word. They’re seen as inevitable blobs of spaghetti code, horrible bloat and painful development experiences. But that doesn’t have to be true.

I’m going to write up a couple of blog posts that go into specific tips for maintaining enterprise ASP.NET monoliths, but I’ll start with some general advice.

All of the following applies to ASP.NET applications on the full .NET Framework, and .NET Core (soon to be known as .NET 5).

Make it Modular, Make it Patchable

The concept of a ‘Modular Monolith’ is not new. If you don’t break your application into multiple libraries (i.e. DLLs), you’re going to get into the world of spaghetti code so fast it will make your source control repository collapse in on itself.

I find that circular reference prevention actually ends up helping to enforce good design patterns, which you do not get if everything is in one big project.

Even if all your code is super tidy, if you’re distributing your software to enterprise customers, at some point you are going to need to patch something, because big customers just don’t upgrade to your latest version very often (once a decade is not that unusual). They certainly aren’t going to just upgrade to the latest build on trunk/master when they find a bug that needs fixing.

If you need to reissue the entire application to patch something, your customer’s internal test teams are going to cry foul, because they can’t predict the impact of your changes, so they’ll say they need to retest the whole thing. They definitely won’t go on trust when you say that all your automated tests pass.

So, to that end, do not build an ASP.NET (v4 or Core) web application that sits in one project (despite what most intro tutorials start off telling you to do). I don’t care what size it is, break it up.

You can add your own Assembly Loading startup process if you need to. The .NET loaders do a great job of loading your references for you, but I find you end up needing a bit more control than you get from the default behaviour. For example, you can explicitly load the libraries of your application based on some manifest file (helpful to control patched DLL versions).

Micro-kernels are your friend

If you can, then build your application using a micro-kernel architecture. By micro-kernel, I mean that there should be a central core of your application that provides base technical support features (data access, logging, dependency injection, etc) but adds no actual functionality to your application.

Once you’ve got that, you can:

  • Update (and patch) blocks of functionality in your application easily. These change much more often than your core.
  • Create customer-specific features (which happens all the time) without polluting your general application code.
  • Develop and test your functionality blocks in isolation.
  • Scale-out your development to multiple teams by giving them different blocks of functionality to work on.

Does that sound familiar? A lot of those advantages are shared with developing microservices; small blocks of functionality with a specific problem domain, that can be developed in isolation.

In terms of deployment we’ve still got one deployment package; it’s your CI system that should bring the Core and Functionality components together into one installer or other package, based on a list of required components for a given customer or branch.

I will say that defining a micro-kernel architecture is very hard to do properly, especially if you have to add it later on, to an existing application architecture.

Pro tip – define your own internal NuGet packages for your Core components, so they can be distributed easily; you can then easily ‘release’ new Core versions to other teams.

If you output NuGet packages from your CI system, you can even have some teams that need Core functionality in development working off an ‘alpha’ build of Core.

Enforce Layer Separation in your APIs
(or ‘if you use a data context in an MVC controller the compiler will slap you’)

Just because everything may be running in one process doesn’t mean you shouldn’t maintain strict separation of layers.

At a minimum, you should define a Business layer that is allowed to access your database, and a UI/Web Service layer, that is not.

The business layer should never consume a UI service, and the UI layer should never directly access/modify data.

Clients of your application should only ever see that UI or Web Service layer.

Escalating terrors in software design.

You could enforce all of this through code reviews, but I find things can still slip through the gaps, so I like to make my API layout do the work in my Core to enforce the layout.

I find a good way to do this in a big .NET application (micro-kernel or otherwise) is to:

  • Define clear base classes that support functionality in each layer.
    For example, create a MyAppBusiness class in your business layer, that all business services must derive from. Similarly, define a MyAppController class that all MVC controllers will derive from (which in turn derives from the normal Controller class).
  • In those classes, expose protected methods to access core services that each layer needs. So your base MyAppBusiness class can expose data access to derived classes, and your MyAppController class can provide localisation/view-rendering support.
  • In your start-up procedure (preferably when you register your Dependency Injection services, if you use it, which you should), only register valid services, that derive from the right base class. Enforce by namespace/assembly if necessary. Throw exceptions if someone has got it wrong.

Where possible, developer mistakes should be detectable/preventable in code. Bake it into your APIs and you can make people follow standards because they can’t do anything if they don’t.

Next Up

In future posts on these sort of topics, I’ll talk about:

  • Tips for working with Entity Framework in applications with a complex database
  • Automated testing of big applications
  • Using PostSharp to verify developer patterns at compile time

..and any other topics that spring to mind.

Easily loading lots of data in parallel over HTTP, using Dataflow in .NET Core

I recently had a requirement to load a large amount of data into an application, as fast as possible.

The data in question was about 100,000 transactions, stored line-by-line in a file, that needed to be sent over HTTP to a web application, that would process it and load it into a database.

This is actually pretty easy in .NET, and super efficient using async/await:

Run that through, and I get a time of 133 seconds; this isn’t too bad right? Around 750 records per second.

But I feel like I can definitely make this better. For one thing, my environment doesn’t look exactly look like the diagram above. It’s a scaled production environment, so looks more like this:

I’ve got lots of resources that I’m not using right now, because I’m only sending one request at a time, so what I want to do is start loading the data in parallel.

Let’s look at a convenient way of doing this, using the System.Threading.Tasks.Dataflow package, which is available for .NET Framework 4.5+ and .NET Core.

The Dataflow components provide various ways of doing asynchronous processing, but here I’m going to use the ActionBlock, which allows me to post messages that are subsequently processed by a Task, in a callback. More importantly, it let’s me process messages in parallel.

Let’s look at the code for my new StreamDataInParallel method:

The great thing about Dataflow is that in only about 18 lines of code, I’ve got parallel processing of data, pushing HTTP requests to a server at a rate of my choice (controlled by the maxParallel parameter).

Also, with the combination of the SendAsync method and specifying a BoundedCapacity, it means I’m only reading from my file when there are slots available in the buffer, so my memory consumption stays low.

I’ve run this a few times, increasing the number of parallel requests each time, and the results are below:

Sadly, I wasn’t able to run the benchmarking tests on the production environment (for what I hope are obvious reasons), so I’m running all this locally; the number of parallel requests I can scale to is way higher in production, but it’s all just a factor of total available cores and database server performance.

Value of maxParallelAverage Records/Second
1750
21293
31785
42150
52500
62777
72941
83125

With 8 parallel requests, we get over 3000 records/second, with a time of 32 seconds to load our 100,000 records.

You’ll notice that the speed does start to plateau (or at least I get diminishing returns); this will happen when we start to hit database contention (typically the main throttling factor, depending on your workload).

I’d suggest that you choose a sensible limit for how many requests you have going so you don’t accidentally denial-of-service your environment; we’ve got to assume that there’s other stuff going on at the same time.

Anyway, in conclusion, Dataflow has got loads of applications, this is just one of them that I took advantage of for my problem. So that’s it, go forth and load data faster!

Displaying Real-time Sensor Data in the Browser with SignalR and ChartJS

In my previous posts on Modding My Rowing Machine, I wired up an Arduino to my rowing machine, and streamed the speed sensor data to an ASP.NET core application.

In this post, I’m going to show you how to take sensor and counter data, push it to a browser as it arrives, and display it in a real-time chart.

If you want to skip ahead, I’ve uploaded all the code for the Arduino and ASP.NET components to a github repo at https://github.com/alistairjevans/rower-mod.

I’m using Visual Studio 2019 with the ASP.NET Core 3.0 Preview for all the server-side components, but the latest stable release of ASP.NET Core will work just fine, I’m not using any of the new features.

Pushing Data to the Browser

So, you will probably have heard of SignalR, the ASP.NET technology that can be used to push data to the browser from the server, and generally establish a closer relationship between the two.

I’m going to use it send data to the browser whenever new sensor data arrives, and also to let the browser request that the count be reset.

The overall component layout looks like this:

Setting up SignalR

This bit is pretty easy; first up, head over to the Startup.cs file in your ASP.NET app project, and in the ConfigureServices method, add SignalR:

Next, create a SignalR Hub. This is effectively the endpoint your clients will connect to, and will contain any methods a client needs to invoke on the server.

SignalR Hubs are just classes that derive from the Hub class. I’ve got just the one method in mine at the moment, for resetting my counter.

Before that Hub will work, you need to register it in your Startup class’ Configure method:

You’re also going to want to add the necessary SignalR javascript to your project. I did it using the “Manage Client-Side Libraries” feature in Visual Studio; you can find my entire libman.json file (which defines which libraries I’m using) on my github repo

Sending Data to the Client

In the MVC Controller where the data arrives from the Arduino, I’m going to push the sensor data to all clients connected to the hub.

The way you access the clients of a hub from outside the hub (i.e. an MVC Controller) is by resolving an IHubContext<THubType>, and then accessing the Clients property.

Pro tip:
Got multiple IO operations to do in a single request, that don’t depend on each other? Don’t just await one, then await the other; use Task.WhenAll, and the operations will run in parallel.

In my example above I’m writing to a file and to SignalR clients at the same time, and only continuing when both are done.

Browser

Ok, so we’ve got the set-up to push data to the browser, but no HTML just yet. I don’t actually need any MVC Controller functionality, so I’m just going to create a Razor Page, which still gives me a Razor template, but without having to write the controller behind it.

If I put an ‘Index.cshtml’ file under a new ‘Pages’ folder in my project, and put the following content in it, that becomes the landing page of my app:

In my site.js file, I’m just going to open a connection to the SignalR hub and attach a callback for data being given to me:

That’s actually all we need to get data flowing down to the browser, and displaying the current speed and counter values!

I want something a little more visual though….

Displaying the Chart

I’m going to use the ChartJS library to render a chart, plus a handy plugin for ChartJS that helps with streaming live data and rendering it, the chartjs-plugin-streaming plugin.

First off, add the two libraries to your project (and your HTML file), plus MomentJS, which ChartJS requires to function.

Next, let’s set up our chart, by defining it’s configuration and attaching it to the 2d context of the canvas object:

Finally, let’s make our chart display new sensor data as it arrives:

With all that together, let’s see what we get!

Awesome, a real-time graph of my rowing!

As an aside, I used the excellent tool by @sarah_edo to generate a CSS grid really quickly, so thanks for that! You can find it at https://cssgrid-generator.netlify.com/

You can check out the entire solution, including all the code for the Arduino and the ASP.NET app, on the github repo at https://github.com/alistairjevans/rower-mod.

Next up for the rowing machine project, I want to put some form of gamification, achievements or progress tracking into the app, but not sure exactly how it will look yet.