(ASP).NET Core in production: Changing log level temporarily - 2nd approach

In the previous blog post I talked about how to change the log level at runtime by coupling the appsettings.json (or rather the IConfiguration) with the ILogger. However, the solution has one drawback: you need to change the file appsettings.json for that. In this post we will be able to change the log level without changing the configuration file.

Want to see some real code? Look at the examples on https://github.com/PawelGerr/Thinktecture.Logging.Configuration 

or just use Nuget packages: Thinktecture.Extensions.Logging.Configuration and Thinktecture.Extensions.Serilog.Configuration 

At first we need a custom implementation of IConfigurationSource and IConfigurationProvider. The actual work does the implementation of IConfigurationProvider. The IConfigurationSource is just to inject the provider into your ConfigurationBuilder.

var config = new ConfigurationBuilder()
    .Add(new LoggingConfigurationSource())
    .Build();
----------------------------------------- public class LoggingConfigurationSource : IConfigurationSource
{
    public IConfigurationProvider Build(IConfigurationBuilder builder)
    {
        // Our implementation of IConfigurationProvider
        return new LoggingConfigurationProvider();
    }
}

As we can see, the LoggingConfigurationSource doesn't do pretty much, let us focus on LoggingConfigurationProvider or rather on the interface IConfigurationProvider.

public interface IConfigurationProvider
{
    bool TryGet(string key, out string value);
    void Set(string key, string value);
    IChangeToken GetReloadToken();
    void Load();
    IEnumerable<string> GetChildKeys(IEnumerable<string> earlierKeys, string parentPath);
}

There are 2 methods that look promising: Set(key, value) for setting a value for a specific key and GetReloadToken() to notify other components (like the logger) about changes in the configuration. Now that we know how to change the configuration values, we need to know the keys and values the logger uses to configure itself. Use Microsoft docs for a hint for Microsoft.Extensions.Logging.ILogger or Serilog.Settings.Configuration in case you are using Serilog.

The pattern for MS-logger key is <<Provider>>:LogLevel:<<Category>>. Here are some examples for the logs coming from Thinktecture components: Console:LogLevel:Thinktecture or LogLevel:Thinktecture.  The value is just one of the Microsoft.Extensions.Logging.LogLevel, like Debug.

namespace Thinktecture
{
    public class MyComponent
    {
        public MyComponent(ILogger<MyComponent> logger)
        {
            logger.LogDebug("Log from Thinktecture.Component");
        }
    }
}

Let's look at the implementation, luckily there is a base class we can use.

public class LoggingConfigurationProvider : ConfigurationProvider
{
    public void SetLevel(LogLevel level, string category = null, string provider = null)
    {
        // returns something like "Console:LogLevel:Thinktecture"
        var path = BuildLogLevelPath(category, provider);
        var levelName = GetLevelName(level); // returns log level like "Debug"

        // Data and OnReload() are provided by the base class
        Data[path] = levelName;
        OnReload(); // notifies other components
    }

    ...
}

Actually, that's it ... You can change the configuration just by setting and deleting keys in the dictionary Data and calling OnReload() afterwards. The only part that's left is to get hold of the instance of LoggingConfigurationProvider to be able to call the method SetLevel from outside but I'm pretty sure you don't need any help for that especially having access to my github repo :)

 

The provided solution does what we intended to, but, do we really want that simple filtering of the logs? Image you are using Entity Framework Core (EF) and there are multiple requests that modify some data. One request is able to commit the transaction the other doesn't and throws, say, an OptimisticConcurrencyException. Your code catches the exception and handles it by retrying the whole transaction, with success. Entity Framework logs this error (i.e. the SQL statement, arguments etc.) internally. The question is, should this error be logged by EF as an Error even if it has been handled by our application? If yes then our logs will be full with errors and it would seem as if we have a lot of bugs in our application. Perhaps it would be better to let EF to log its internal errors as Debug, so that this information is not lost and if our app can't handle the exception then we will log the exception as an error.

But that's for another day ...


.NET Core in production: Changing log level temporarily

When running the application in production then the log level is set somewhere between Information and Error. The question is what to do if you or your customer experiences some undesired behavior and the logs with present log level aren't enough to pinpoint the issue.

The first solution that comes to mind is to try to reproduce the issue on a developer's machine with lower log level like Debug. It may be enough to localize the bug but sometimes it isn't. Even if you are allowed to restart the app in production with lower log level, the issue may go away ... temporarily, i.e. the app still has a bug.

Better solution is to change the log level temporarily without restarting the app.

First step is to initialize the logger with the IConfiguration. That way the logger changes the level as soon as you change the corresponding configuration (file).

In this post I will provide 2 examples, one that is using the ILoggingBuilder of the ASP.NET Core and the other example is using Serilog because it is not tightly coupled to ASP.NET Core (but works very well with it!). 

Using ILoggingBuilder:

// the content of appsettings.json
{
    "Logging": {
        "LogLevel": { "Default": "Information" }
    }
}
-----------------------------------------
var config = new ConfigurationBuilder().
    AddJsonFile("appsettings.json", false, true) // reloadOnChange=true
    .Build();
// Setup of ASP.NET Core application WebHostWebHost
    .CreateDefaultBuilder()
    .ConfigureLogging(builder =>
    {
        builder.AddConfiguration(config); // <= init logger
        builder.AddConsole();
    })
    ...

Using Serilog:

// the content of appsettings.json
{
    "Serilog": {
        "MinimumLevel": { "Default": "Information" }
    }
}
-----------------------------------------
var config = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json", false, true) // reloadOnChange=true
    .Build();
var serilogConfig = new LoggerConfiguration()
    .ReadFrom.Configuration(config) // <= init logger
    .WriteTo.Console();

In case you are interested in integration with (ASP).NET Core 

// If having a WebHost
WebHost
    .CreateDefaultBuilder()
    .ConfigureLogging(builder =>
    {
        builder.AddSerilog(serilogConfig.CreateLogger());
    })
    ...;

// If there is no WebHost
var loggerFactory = new LoggerFactory()
    .AddSerilog(serilogConfig.CreateLogger());

At this point the loggers are coupled to IConfiguration or rather to appsettings.json, i.e if you change the level to Debug the app starts emitting debug-messages as well, without restarting it.

This solution has one downside, you need physical access to the appsettings.json. Even if you do, it still would be better to not change the configuration file. What we want is a component that let us set and reset a temporary log level and if this temporary level is not active then the values from appsettings.json should be used. That way you can change the level from the GUI or via an HTTP request against the Web API of your web application.

Luckily, the implementation effort for that feature is pretty low, but that's for another day...


ASP.NET Core: Update to Autofac 4.6.1 recommended - more than a bugfix release

If you are using Autofac in your ASP.NET Core application then I recommend to update Autofac to version 4.6.1. This bugfix release brought a change how child scope handle additional registrations so that some errors like Cannot resolve parameter 'IOptionsFactory<KestrelServerOptions>' just disappear.

With additional registrations I mean the following:

var builder = new ContainerBuilder();

using (var container = builder.Build())
{
    using (var childScope = container.BeginLifetimeScope(innerBuilder =>
        {
            // additional registration that are known by childScope only
            innerBuilder.RegisterType<Foo>().AsSelf();
        }))
    {
        ...
    }
}

When using a child scope during the setup of an ASP.NET Core application having Autofac 4.6.0 or lower then you had to use the IContainer (= root scope) itself or use a workaround to register MVC components. The first option is not recommended because a DI container should be considered as immutable. The second option could be confusing if you don't know the internals of Autofac.

The second options looks like this:

public class Startup
{
    public Startup(ILifetimeScope childScope)
    {
        _childScope = childScope;
    }

    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();

        // All singleton are "rebased" to "aspNetScopeTag"
        const string aspNetScopeTag = "AspNetScope";

        _aspNetScope = _childScope.BeginLifetimeScope(aspNetScopeTag,
            builder => builder.Populate(services, aspNetScopeTag));

         return new AutofacServiceProvider(_aspNetScope);
    }
    ...
}

 With Autofac 4.6.1 you just call Populate without any confusing parameters like aspNetScopeTag.

public IServiceProvider ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    _aspNetScope = _childScope.BeginLifetimeScope(builder => builder.Populate(services));

    return new AutofacServiceProvider(_aspNetScope);
}

 

Want to try it out? I have prepared 2 projects: AspNetCore2_Autofac460 and AspNetCore2_Autofac461 in my github repo. Download the sources and start the applications with dotnet run in the corresponding folder. The first one will raise an error the latter will start the web server successfully.

 

P.S.: Missing context? Why someone would want to provide its own DI container instead of using IServiceCollection and be done with it? Then read my blog post ASP.NET Core in production: Take back control of your web app or the follow-up post ASP.NET Core in production: Graceful shutdown and reacting to aborted requests.


ASP.NET Core in production: Graceful shutdown and reacting to aborted requests

In the previous post "ASP.NET Core in production: Take back control of your web app" I mentioned that getting hold if the dependency injection (DI) is just one step of many to improve the architecture of your web applications. Today well will look into 2 other aspects that are best explained together: graceful shutdown and reacting to aborted requests.

Image you are in the middle of handling of a web request. How do you know that the WebHost is about to shut down or that the client is not interested in the response anymore? Especially the latter will occur more often and without proper handling you end up wasting resources for nothing.

1) If your component is interested in server shutdown only then you can inject IApplicationLifetime and use the property ApplicationStopping that is of type CancellationToken. Most of asynchronous operations support cancellation tokens so you just have to pass the token to them but be prepare for handling the OperationCanceledException and TaskCanceledException.

public DemoController(IApplicationLifetime appLifetime)
{
    _appLifetime = appLifetime;
}     
[HttpGet("MyAction")]
public async Task<IActionResult> MyAction()
{
    CancellationToken token = _appLifetime.ApplicationStopping;
    return await DoSomethingAsync(token);
}

Having a side-free operation like selecting data from database can be cancelled without much effort just pass the CancellationToken to asynchronous methods (like ToListAsync(token)) or check the property CancellationToken.IsCancellationRequested in case you are iterating over some collection or does other synchronous stuff. However, if the operation is critical, the server is shutting down and the component needs a few seconds more to finish then you can delay the shutdown by registering a callback with the CancellationToken.

Use this feature with care, don't hold up the shutdown indefinitely!

_appLifetime.ApplicationStopping.Register(() =>
{
    // server is not going to shutdown     // until the callback is done
});

Note: you can delay the shutdown by overriding the method Dispose of the controller. Using IDisposable-components to finish some stuff works best if you decouple the DI from ASP.NET life cycle like suggested in my previous post because in most cases you want(!) to shut down the WebHost first so that the endpoints are closed and no new requests can come in. Afterwards you can stop your internal processes and dispose of components.

public class DemoController: Controller
{
    protected override void Dispose(bool disposing)
    {
        base.Dispose(disposing);
        //finish your stuff synchronously
    }
}

2) Being interested in server shutdown only is more of an edge case. Most of the time it doesn't matter whether the operation has to stop because of the shutdown or because the client has aborted the HTTP request (e.g. by navigating to another page). In both cases you can use HttpContext.RequestAborted which is another CancellationToken. Although it is called "request-aborted" it is cancelled on shutdown as well because in this case the request is aborted by the server itself not the client.

[HttpGet("MyAction")]
public async Task<IActionResult> MyAction()
{
    CancellationToken token = HttpContext.RequestAborted;
    return await DoSomethingAsync(token);
}

If the method happens to be an action of an MVC/Web API controller (like in my examples) then you can just specify a new method argument of type CancellationToken to get HttpContext.RequestAborted provided to you.

[HttpGet("MyAction")]
public async Task<IActionResult> MyAction(CancellationToken token)
{
    return await DoSomethingAsync(token);
}

 

Want to try it out? I've updated the example from my previous post so that you are able to start and abort HTTP requests or restart the WebHost. The sources are available on Github.

ASP.NET Core - Graceful shutdown


ASP.NET Core in production: Take back control of your web app

With ASP.NET Core the setup of a new MVC or a Web API project is essentially a 2-liner. The standard setup you can find in most tutorials is ideal for demos, workshops or simple web apps but is insufficient for complex applications.

The standard setup looks as follows:

WebHost
.CreateDefaultBuilder()
.UseStartup<Startup>()
.Build()
.Run();

The rest of the configuration of the web app is usually made in Startup.

The problem with the standard approach is that the WebHost controls your entire web application, i.e. it dictates the app life cycle, sets up dependency injection framework, starts and stops your processes and if the WebHost is shutting down (or crashes) then your web application is down as well. Especially the stopping of the app is more of a crash than a graceful shutdown.

A web application is usually more than just a bunch of endpoints. Depending on your requirements you may need a more complex startup, perhaps consisting of multiple steps like

  • setting up logging first
  • pulling configuration
  • filling caches
  • processing of (temp) data from previous run
  • starting some tasks like listening on the file system or for pulling the requests from a message queue because HTTP is not the only gateway to the world
  • etc.

Only after all preparation steps are made the app is ready to open the HTTP endpoints to accept new requests, i.e. to start the WebHost. The web app may run for weeks or month so you may want to restart it (automatically) multiple times to reconfigure the ASP.NET Core pipeline without killing the whole process. And, if the app has to be stopped then you probably want to close the endpoints first, give running requests some time to finish, process the data in buffers or temp files, log some statistics and only then to stop the application.

The first step to get these features is to get control over dependency injection (DI) managing your components. Giving full control of the DI to the ASP.NET Core runtime means that you can't bootstrap your processes before starting the WebHost and if it is stopped then the DI container incl. all your components is disposed of as well. This makes a graceful shutdown very hard. 

In the following examples I'm using Autofac because it is one of the feature-richest and most mature dependency injection frameworks available for .NET. Especially, with Autofac v4.6.1 and Autofac.Extensions.DependencyInjection is it very easy to achieve the desired setup.

Example of a more complex startup so it is easier to get the idea what I 'm talking about: (pseudo code)

var builder = new ContainerBuilder();
// register all dependencies specific to your app
// not necessarilly specific to ASP.NET ...
using (var container = builder.Build())
{
    // assume, depending on some configurations
    // you need to add further dependencies
    var myConfig = container.Resolve<MyConfiguratio>();

    // Adding further dependencies on a child scope
    // can be repeated multiple times if required
    using (var appScope =
        container.BeginLifetimeScope(appScopeBuilder => RegisterFurtherDeps(myConfig)))
    {
        // All methods should do their work in child scopes of "appScope"
        // which should be disposed eventually to free the resources.

        // For example, check file system permissions, databases etc.
        CheckPreconditions(appScope);
        CleanupTheMessFromPreviousRun(appScope);
        StartSomeKindOfTasks(appScope);

        await StartWebHostAsync(appScope);

        // when we are here then that means the endpoints are closed
        WaitForPendingRequests(maxWaitTime: Timespan.FromSeconds(5));
        Cleanup();
        LogStatistics();
    }
}

....

public async Task StartWebHostAsync(ILifetimeScope appScope)
{
    // The WebHost gets its own scope and can do with it what ever it wants,
    // like "polluting" it with some ASP.NET stuff :)
    using (var webHostScope =
        appScope.BeginLifetimeScope(builder => RegisterAspNetSpecificStuff(builder)))
    {
        await WebHost
        .CreateDefaultBuilder()
        .UseStartup<Startup>()
         // This method does not exist but you get the point
        .UseThisDependencyInjectionScope(webHostScope) 
         .Build()
        .RunAsync();
    }
}

 

After a lengthly motivational part let's get to the real code.

In this demo we will setup an MVC web and restart the WebHost on button-click just to proof that the application stays alive even if the web server goes down. 

 At first we set up a DI container and register a class MySingleton as a singleton. This class will be the proof that it is not going to be recreated every time the WebHost restarts.

private static async Task StartWebAppAsync()
{
    var builder = new ContainerBuilder();
    builder.RegisterType<MySingleton>().AsSelf().SingleInstance();
    
    using (var container = builder.Build())
    {
        while (true)
       {
           await StartWebServerAsync(container);
       }
   }
}

The configuration of the WebHost differs just in one point: the instantiation of the class Startup is delegated to Autofac. Because of that our Startup is getting ILifetimeScope via constructor injection.

private static async Task StartWebServerAsync(ILifetimeScope scope)
{
    using (var webHostScope = scope.BeginLifetimeScope(
        builder => builder.RegisterType<Startup>().AsSelf()))
   {
       await WebHost
        .CreateDefaultBuilder()
        .UseStartup<Startup>()
        .ConfigureServices(services =>
            services.AddTransient(provider => webHostScope.Resolve<Startup>()))
        .Build()
        .RunAsync();
   }
}

Note: If your Startup has some ASP.NET specific dependencies, say IHostingEnvironment and IConfiguration then you have to pull them out of IServiceProvider and pass them to a factory created by Autofac.

public Startup(ILifetimeScope webHostScope, 
    IHostingEnvironment hostingEnvironment,
    IConfiguration configuration)
{
    ...
}

...
await WebHost
    ...
    .ConfigureServices(services => services.AddTransient(provider =>
    {
        var hostingEnv = provider.GetRequiredService<IHostingEnvironment>();
        var config = provider.GetRequiredService<IConfiguration>();
        var factory = webHostScope.Resolve<Func<IHostingEnvironment, IConfiguration, Startup>>();          return factory(hostingEnv, config);
    }))
    ...

Ok, back on track.  

In the Startup we copy the DI registrations from IServiceCollection to our DI container by using the method Populate and save the newly created DI scope in a variable so it can be disposed when the ASP.NET part is shutting down.

public IServiceProvider ConfigureServices(IServiceCollection services)
{
    // register ASP.NET Core stuff
    services.AddMvc();
    _aspNetScope = _webHostScope.BeginLifetimeScope(builder => builder.Populate(services));     return new AutofacServiceProvider(_aspNetScope);
}
public void Configure(IApplicationBuilder app, IApplicationLifetime appLifetime)
{
    app.UseMvc();
    appLifetime.ApplicationStopped.Register(() => _aspNetScope.Dispose());
}

 

Your MVC application is now set up properly.

Want to try it out? Download the sources from Github, launch the mini MVC app with dotnet run, go to the url printed in the console window and start restarting the WebHost :)

ASP.NET Core - Take back the control of your web app

 

P.S.: Getting hold of the DI is just one but not the only one aspect of keeping your app under control. But that's a topic for another day...


ASP.NET Core: Beware - Singleton may not be singleton

If you register a type as a singleton then you expect just 1 instance of this type in your whole application. What you may not know is that ASP.NET Core is creating 2 instances of IServiceProvider during building of the IWebHost that may lead to 2 instance of your "singleton".

This is the case if you register a type, say MySingleton, when configuring the web host ...

WebHost
.CreateDefaultBuilder()
.UseStartup<Startup>()
.ConfigureServices(services => services.AddSingleton<MySingleton>())
.Build()
.Run();

 ..., e.g. so that is available in the constructor of your Startup

public class Startup
{
private readonly MySingleton _mySingletonFromHostingServiceProvider;
public Startup(MySingleton mySingletonFromHostingServiceProvider)
{
_mySingletonFromHostingServiceProvider = mySingletonFromHostingServiceProvider;
}
...
}

Now, if we resolve MySingleton during normal web request we get a whole new instance instead the same instance as in constructor of the Startup class. 

public void Configure(IApplicationBuilder app)
{
app.Use((ctx, next) =>
{
var mySingleton = ctx.RequestServices.GetRequiredService<MySingleton>();

// the comparison of 2 instances yields "false"
var areEqual = _mySingletonFromHostingServiceProvider == mySingleton;

Console.WriteLine($"==> {nameof(_mySingletonFromHostingServiceProvider)} == {nameof(mySingleton)}: {areEqual}");          return next();
    });
}

 There are at least two ways to fix this problem.

Either pass an instance of MySingleton to method AddSingleton instead of passing just the type

var mySingleton = new MySingleton();
WebHost
.CreateDefaultBuilder()
.UseStartup<Startup>()
.ConfigureServices(services => services.AddSingleton(mySingleton))
.Build()
.Run();

 or by replacing the previous registration with a new one in ConfigureServices 

public class Startup
{
private readonly MySingleton _mySingletonFromHostingServiceProvider;
public Startup(MySingleton mySingletonFromHostingServiceProvider)
{
_mySingletonFromHostingServiceProvider = mySingletonFromHostingServiceProvider;
}
public void ConfigureServices(IServiceCollection services)
{
services.Replace(new ServiceDescriptor(typeof(MySingleton), _mySingletonFromHostingServiceProvider));
  // alternative way
//services.AddSingleton(_mySingletonFromHostingServiceProvider);
}
...
}

 

According to @davidfowl the ASP.NET team will address this problem in the future. 

PS: There is at least another one solution to fix this problem and gaining back the control over your web app but that's for another time ... :)

 

 


ASP.NET Core Web API Performance - Throughput for Upload and Download

After working with the new ASP.NET Core server Kestrel and the HttpClient for a while in a number of projects I run into some performance issues. Actually, it was a throughput issue.
It took me some time to figure out whether it is the server or the client responsible for the problems. And the answer is: both.

Here are some hints to get more out of your web applications and Web APIs.

The code for my test server and client are on GitHub: https://github.com/PawelGerr/AspNetCorePerformance

In the following sections we will download and upload data using different schemes, storages and parameters measuring the throughput.

Download data via HTTP

Nothing special, we download a 20 MB file from the server using the default FileStreamResult:

[HttpGet("Download")]
public IActionResult Download()
{
    return File(new MemoryStream(_bytes), "application/octet-stream");
}

The throughput on my machine is 140 MB/s.
For the next test we are using a CustomFileResult with increased buffer size of 64 KB and suddenly get a throughput of 200 MB/s.

Upload multipart/form-data via HTTP

The ASP.NET Core introduced a new type IFormFile that enables us to receive multipart/form-data without any manual work. For that we create a new model with a property of type IFormFile and use this model as an argument of a Web API method.

public class UploadMultipartModel
{
    public IFormFile File { get; set; }
    public int SomeValue { get; set; }
}

-------------

[HttpPost("UploadMultipartUsingIFormFile")]
public async Task<IActionResult> UploadMultipartUsingIFormFile(UploadMultipartModel model)
{
    var bufferSize = 32 * 1024;
    var totalBytes = await Helpers.ReadStream(model.File.OpenReadStream(), bufferSize);

    return Ok();
}

-------------

public static async Task<int> ReadStream(Stream stream, int bufferSize)
{
    var buffer = new byte[bufferSize];

    int bytesRead;
    int totalBytes = 0;

    do
   {
       bytesRead = await stream.ReadAsync(buffer, 0, bufferSize);
        totalBytes += bytesRead;
    } while (bytesRead > 0);
    return totalBytes;
}

Using the IFormFile to transfer 20 MB we get a pretty bad throughput of 30 MB/s. Luckily we got another means to get the content of a multipart/form-data request, the MultipartReader.
Having the new reader we are able to improve the throughput up to 350 MB/s.

[HttpPost("UploadMultipartUsingReader")]
public async Task<IActionResult> UploadMultipartUsingReader()
{
    var boundary = GetBoundary(Request.ContentType);
    var reader = new MultipartReader(boundary, Request.Body, 80 * 1024);

    var valuesByKey = new Dictionary<string, string>();
    MultipartSection section;

    while ((section = await reader.ReadNextSectionAsync()) != null)
    {
        var contentDispo = section.GetContentDispositionHeader();

        if (contentDispo.IsFileDisposition())
       {
            var fileSection = section.AsFileSection();
            var bufferSize = 32 * 1024;
            await Helpers.ReadStream(fileSection.FileStream, bufferSize);
        }
        else if (contentDispo.IsFormDisposition())
        {
            var formSection = section.AsFormDataSection();
            var value = await formSection.GetValueAsync();
            valuesByKey.Add(formSection.Name, value);
        }
    }

    return Ok();
}

private static string GetBoundary(string contentType)
{
    if (contentType == null)
        throw new ArgumentNullException(nameof(contentType));

    var elements = contentType.Split(' ');
    var element = elements.First(entry => entry.StartsWith("boundary="));
    var boundary = element.Substring("boundary=".Length);

    boundary = HeaderUtilities.RemoveQuotes(boundary);

    return boundary;
}

Uploading data via HTTPS

In this use case we will upload 20 MB using different storages (memory vs file system) and different schemes (http vs https).

The code for uploading data:

var stream = readFromFs
    ? (Stream) File.OpenRead(filePath)
    : new MemoryStream(bytes);

var bufferSize = 4 * 1024; // default

using (var content = new StreamContent(stream, bufferSize))
{
    using (var response = await client.PostAsync("Upload", content))
    {
        response.EnsureSuccessStatusCode();
    }
}

Here are the throughput numbers:

  • HTTP + Memory: 450 MB/s
  • HTTP + File System: 110 MB
  • HTTPS + Memory: 300 MB/s
  • HTTPS + File System: 23 MB/s

Sure, the file system is not as fast as the memory but my SSD is not that slow to get just 23 MB/s .... let's increase the buffer size instead of using the default value of 4 KB.

  • HTTPS + Memory + 64 KB: 300 MB/s
  • HTTPS + File System + 64 KB: 200 MB/s
  • HTTPS + File System + 128 KB: 250 MB/s

With bigger buffer size we get huge improvements when reading from slow storages like the file system.

Another hint: Setting the Content-Length on the client yields better overall performance.

Summary

When I startet to work on the performance issues my first thought was that Kestrel is to blame because it had not enough time to mature yet.  I even tried to place IIS in front of Kestrel so that IIS is responsible for HTTPS stuff and Kestrel for the rest. The improvements are not worth of mentioning. After adding a bunch of trace logs, measuring time on the client and server, switching between schemes and storages I realized that the (mature) HttpClient is causing issues as well and one of the major problem were the default values like the buffer size.

 


Entity Framework Core Migrations: Assembly Version Mismatch

If you have switched your .NET Core project from xproj to csproj (MSBuild) and updated the nuget packages then you may run into an issue when executing some of the dotnet ef-commands.

I got the following error after executing dotnet ef migrations list:

Could not load file or assembly 'Microsoft.EntityFrameworkCore, Version=1.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)

The problem is that some of my (3rd party) dependencies are using version 1.1.0 and the others version 1.1.1. In a classic .NET 4.6 project we use assembly redirects to solve this kind of problems and the in .NET Core we do the same ...

Just create an app.config file with the following content:

<?xml version="1.0" encoding="utf-8"?>

<configuration>
    <runtime>
        <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
            <dependentAssembly>
                <assemblyIdentity name="Microsoft.EntityFrameworkCore" culture="neutral" publicKeyToken="adb9793829ddae60" />
                <bindingRedirect oldVersion="0.0.0.0-1.1.1.0" newVersion="1.1.1.0" />"
            </dependentAssembly>
            <dependentAssembly>
                <assemblyIdentity name="Microsoft.EntityFrameworkCore.Relational" culture="neutral" publicKeyToken="adb9793829ddae60" />
                <bindingRedirect oldVersion="0.0.0.0-1.1.1.0" newVersion="1.1.1.0" />"
            </dependentAssembly>
            <dependentAssembly>
                <assemblyIdentity name="Microsoft.Extensions.Logging.Abstractions" culture="neutral" publicKeyToken="adb9793829ddae60" />
                <bindingRedirect oldVersion="0.0.0.0-1.1.1.0" newVersion="1.1.1.0" />"
            </dependentAssembly>
        </assemblyBinding>
    </runtime>
</configuration>

If you still getting errors than make sure you have the following items in your csproj-file

<ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="1.1.0">
        <PrivateAssets>All</PrivateAssets>
    </PackageReference>
    <DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.0" />
</ItemGroup>

 


Strongly-typed Configuration for .NET Core - with full Dependency Injection support

Configuration is one of the most prominent cornerstones in software systems, and especially in distributed systems. And it has been a point for discussions in .NET for quite some time.

In one of our projects we have built a solution that lets different applications in different companies exchange data, although being behind firewalls, using the open source Relay Server. But, to our surprise, one of the features our customer was amazed about was the library I've developed to make configuration easier to handle.

In Thinktecture.Configuration I've taken over the ideas, generalized them and added new features.

The basic idea is that .NET developers should be able to deal with configuration data by just using arbitrary classes.

The library consists of 3 main components: IConfigurationLoader, IConfigurationProvider and IConfigurationSelector. The IConfigurationLoader loads data from storage, e.g. JSON from the file system. The IConfigurationProvider uses IConfigurationSelector to select the correct piece of data like a JSON property and to convert this data to requested configuration.

Architecture

In short, the features of the lib are:

  • The configuration (i.e. the type being used by the application)
    • should be type-safe
    • can be an abstract type (e.g. an interface)
    • don't have to have property setters or other kind of metadata just to be deserializable
    • can be and have properties that are virtually as complex as needed
    • can be injected via DI (dependency injection)
    • can have dependencies that are injected via DI (i.e. via constructor injection)
    • can be changed by "overrides"
  • The usage of the configuration in a developer's code base should be free of any stuff from the configuration library
  • The extension of a configuration by new properties or changing the structure of it should be a one-liner

Use Cases

In this post I'm going to show the capabilities of the library by illustrating it with a few examples. In this concrete example I'am using a JSON file containing the configuration values (with Newtosonf.Json in the background) and Autofac for DI.

But the library is not limited to these. The hints are at the end of this post if you want to use a different DI framework, other storage than the file system (i.e. JSON files) or not use JSON altogether.

The first use case is a bit lengthy to explain the basics. The others will just point out specific features.

Want to see the code? Go to Thinktecture.Configuration

Nuget: Install-Package Thinktecture.Configuration.JsonFile.Autofac

1. One file containing one or more configurations

Shown features in this example:

  • one JSON file
  • multiple configurations
  • a configuration doesn't have to be on the root of the JSON file
  • a configuration has dependencies known by DI
  • a configuration gets injected in a component like any other dependency

We start with 2 simple configuration types.

public interface IMyConfiguration
{
    string Value { get; }
}

public interface IOtherConfiguration
{
    TimeSpan Value { get; }  
}

The configuration IMyConfiguration is required by our component MyComponent.

public class MyComponent
{
    public MyComponent(IMyConfiguration config)
    {
    }
}

 Configuration file configuration.json

{
    "My":
    {
        "Config": { value: "content" }
    },
    "OtherConfig": { value: "00:00:05" }
}

Now let's setup the code in the executing assembly and configure DI to make MyComponent resolvable along with IMyConfiguration .

var builder = new ContainerBuilder();
builder.RegisterType<MyComponent>().AsSelf();

// IFile is required by JsonFileConfigurationLoader to access the file system
// For more info: https://www.nuget.org/packages/Thinktecture.IO.FileSystem.Abstractions/
builder.RegisterType<FileAdapter>().As<IFile>().SingleInstance();

// register so-called configuration provider that operates on "configuration.json"
builder.RegisterJsonFileConfigurationProvider("./configuration.json");

// register the configuration.
// "My.Config" is the (optional) path into the config JSON structure because our example configuration does not start at the root of the JSON
builder.RegisterJsonFileConfiguration<MyConfiguration>("My.Config")
    .AsImplementedInterfaces() // i.e. as IMyConfiguration
    .SingleInstance(); // The values won't change in my example

// same difference with IOtherConfiguration
builder.RegisterJsonFileConfiguration<OtherConfiguration>("OtherConfig")
    .AsImplementedInterfaces();

var container = builder.Build();

 The concrete types MyConfiguration and OtherConfiguration are, as often when working with abstractions, used with DI only. Apart from that, these types won't show up at any other places. The type MyConfiguration has a dependency IFile that gets injected during deserialization.

public class MyConfiguration : IMyConfiguration
{
    public string Value { get; set; }

    public MyConfiguration(IFile file)
    {
        ...
    }
}

public class OtherConfiguration : IOtherConfiguration
{
    public TimeSpan Value { get; set; }  
}

 The usage is nothing special

// IMyConfiguration gets injected into MyComponent
var component = container.Resolve<MyComponent>();

// we can resolve IMyConfiguration directly if we want to
var config = container.Resolve<IMyConfiguration>();

2. Nesting

Shown features in this use case:

  • one of the properties of a configuration type is a complex type
  • complex property type can be instantiated by Newtonsoft.Json or DI
  • complex property can be resolved directly if required

In this example IMyConfiguration has a property that is not of a simple type. The concrete types implementing IMyConfiguration and IMyClass consist of property getters and setters only thus left out for brevity.

public interface IMyConfiguration
{
    string Value { get; }
    IMyClass MyClassValue { get; }
}

public interface IMyClass
{
    int Value { get; }  
}

The JSON file looks as following:

{
    "Value": "content",
    "MyClassValue": { "Value": 42 }
}

Having a complex property we can decide whether the type IMyClass is going to be instantiated by Newtonsoft.Json or DI.

With just the following line the type IMyClass is not introduced to the configuration library and is going to be instantiated by Newtonsoft.Json.

builder.RegisterJsonFileConfiguration<MyConfiguration>()
    .AsImplementedInterfaces()
    .SingleInstance();

With the following line we introduce the type to the config lib and DI but the instance of IMyClass cannot be resolved directly.

builder.RegisterJsonFileConfigurationType<MyClass>();

Should IMyClass be resolvable directly then we can use the instance created along with IMyConfiguration or let new instance be created.

// option 1: use the property of IMyConfiguration
builder.Register(context => context.Resolve<IMyConfiguration>().MyClassValue)
    .AsImplementedInterfaces()
    .SingleInstance();

// option 2: let create a new instance
builder.RegisterJsonFileConfiguration<MyClass>("MyClassValue")
    .AsImplementedInterfaces()
    .SingleInstance();

 3. Multiple JSON files

The configurations can be loaded from more than one file.

Configuration types are

public interface IMyConfiguration
{
    string Value { get; }
}

public interface IOtherConfiguration
{
    TimeSpan Value { get; }  
}

File myConfiguration.json

{
    "Value": "content"
}

File otherConfiguration.json

{
    "Value": "00:00:05"
}

Having two files we need a means to distinguish between them when registering the configurations. In this case we use RegisterKeyedJsonFileConfigurationProvider that returns a key that will be passed to RegisterJsonFileConfiguration.

var providerKey = builder.RegisterKeyedJsonFileConfigurationProvider("myConfiguration.json");
builder.RegisterJsonFileConfiguration<MyConfiguration>(providerKey)
    .AsImplementedInterfaces()
    .SingleInstance();

var otherKey = builder.RegisterKeyedJsonFileConfigurationProvider("otherConfiguration.json");
builder.RegisterJsonFileConfiguration<OtherConfiguration>(otherKey)
    .AsImplementedInterfaces()
    .SingleInstance();

4. Overrides

A configuration can be assembled from one base configuration and one or more overrides.

In this case we have two config files. One containing the default values of the configuration and the other containing values to override.

Default values come from baseConfiguration.json

{
    "Value":
    {
        "InnerValue_1": 1,
        "InnerValue_2": 2
    }
}

InnerValue_2 will be changed by the overrides.json

{
    "Value":
    {
        "InnerValue_2": 3
    }
}

The configuration 

public interface IMyConfiguration
{
    IInnerConfiguration Value { get; }  
}

public interface IInnerConfiguration
{
    int InnerValue_1 { get; }
    int InnerValue_2 { get; }
}

To specify overrides we need to provide more than one file path when registering the configuration provider. The overrides are applied in the same order they passed to RegisterJsonFileConfigurationProvider.

builder.RegisterJsonFileConfigurationProvider("baseConfiguration.json", "overrides.json");
builder.RegisterJsonFileConfiguration<MyConfiguration>()
    .AsImplementedInterfaces()
    .SingleInstance();

5. Extension of the configuration

Let's add a property to IInnerConfiguration from previous paragraph.

public interface IInnerConfiguration
{
    int InnerValue_1 { get; }
    int InnerValue_2 { get; }
    string NewValue { get; }
}

Add the corresponding property to the JSON file baseConfiguration.json

{
    "Value":
    {
        "InnerValue_1": 1,
        "InnerValue_2": 2,
        "NewValue": "content"
    }
}

That's it.

Working with different frameworks, storages and data models

Using another DI framework

To use a different DI framwork than Autofac use the package Thinktecture.Configuration.JsonFile instead of Thinktecture.Configuration.JsonFile.Autofac and implement the interface IJsonTokenConverter using your favorite DI framework.  The converter has just one method TConfiguration Convert<TConfiguration>(JToken token).

Load JSON from other media

To load JToken from other storages than the file system just implement the interface IConfigurationLoader<JToken,JToken>. For example, if the JSON configuration are in a database then inject the database context or a data access layer and select corresponding data rows.

Use different data models

If you are using other data model than JSON then reference the package Thinktecture.Configuration and implement the interfaces IConfigurationLoader<TRawDataIn,TRawDataOut>, IConfigurationProvider<TRawDataIn,TRawDataOut> and IConfigurationSelector<TRawDataIn,TRawDataOut>. It sounds like much but if you look into the code of the corresponding JSON-based classes you will see that the classes are pretty small and trivial.

Some final words...

Although the configuration is an important part of the software development it is not the most exciting one. Therefore, a software developer may be inclined to take shortcuts and work with meaningful hardcoded values. Thinktecture.Configuration gives you the means to work with .NET types without thinking too much how to load and parse the values. This saves time, improves the reusability of the components and the software architecture.


ASP.NET Core with IIS: Setup Issues

If you are planing to run an ASP.NET Core application with IIS then this blog post might be worth a glance.

These are a few issues I run into ...

1. Targets in .xproj-file

If the project started with RC1 or earlier version of .NET Core then check for correct targets. Open the .xproj file and search for the following line

<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" 
        Condition="'$(VSToolsPath)' != ''" />

and replace it with

<Import Project="$(VSToolsPath)\DotNet.Web\Microsoft.DotNet.Web.targets" 
        Condition="'$(VSToolsPath)' != ''" />

2. The process path in web.config

If you get 502 after starting the web then take a look into windows event viewer. One of the errors you will probably see is

Application 'MACHINE/WEBROOT/APPHOST/YOUR-APP with physical root 'C:\webapp\publish\' created process with commandline '"dotnet" WebApp.Selfhost.dll' but either crashed or did not reponse or did not listen on the given port '28236', ErrorCode = '0x800705b4'

This error means that IIS is unable to start your app using the command dotnet. To remedy this issue open web.config and change the processPath from dotnet to C:\Program Files\dotnet\dotnet.exe.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <system.webServer>
        <handlers>
            <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
        </handlers>
        <aspNetCore processPath="C:\Program Files\dotnet\dotnet.exe"
            arguments=".\WebApp.Selfhost.dll"
            stdoutLogEnabled="false"
            stdoutLogFile=".\logs\stdout"
            forwardWindowsAuthToken="false" />
    </system.webServer>
</configuration>

3. When to call UseIISIntegration

If you still getting 502 then the possible cause of this error may be that the your application is listening on a different port as expected. This can happen if one of your configuration keys is Port. In this case your web is listening on this port instead of dynamically generated port.

The configuration of the WebHostBuilder causing the error can look as following:

var hostBuilder = new WebHostBuilder()
    .UseConfiguration(myConfig) // inserts config with key "Port"
    .UseIISIntegration()    // uses previously inserted port "by mistake"
    .UseKestrel()
    .UseStartup<Startup>();

To cure that just change the order of the calls because with .NET Core 1.1 the listening url when running with IIS will not been overwritten anymore.

var hostBuilder = new WebHostBuilder()
    .UseIISIntegration()
    .UseConfiguration(myConfig)
    .UseKestrel()
    .UseStartup<Startup>();