How to Dependency Inject a Service from Startup back in Program

Full source code here.

While writing some recent blog posts on HttpClientFactory I had to work with some of the obscure features of ServiceCollection and dependency injection in .NET Core 2.1. One of the things I wanted to do was get at an instance of a token generator from the service collection inside the Main method in Program.cs.

Some time ago I wrote a post about using the ServiceCollection from Program.cs and while that worked for the example I gave, there are cases where it would not depending on the specifics of what scope you are using, and in the case of a singleton, how you instanced it. When it goes wrong you end up with multiple instances of a singleton, multiple DI containers and plenty of unpredictable results. It’s too painful to explain all the variations, you can read a bit more here where David Folwer says “hosting current[ly] creates 3 service providers and its extremely broken in the general case”.

How to Pass a Service from ServicesCollection into Program
In this approach none there are none of the problems outlined above, only one instance of the singleton is every instantiated.

In Startup.cs add all the services you want inside ConfigureServices(..).

public void ConfigureServices(IServiceCollection services)
{
    services.AddMemoryCache();
    services.AddSingleton<ITokenGenerator, TokenGenerator>();
}

In Program.cs, I have the usual CreateWebHostBuilder(..), no changes.

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)	
        .UseStartup<Startup>();	

But in Main, instead of

CreateWebHostBuilder(args).Build().Run();

I split building the WebHost from running the WebHost.

public static void Main(string[] args)
{		
    IWebHost webHost = CreateWebHostBuilder(args).Build();
    var tokenGenerator = webHost.Services.GetService<ITokenGenerator>();
    string token =  tokenGenerator.GetToken();
    System.Console.WriteLine(token);
    webHost.Run();
}
  • Line 3 creates the webHost, but doesn’t run it
  • this in turn instantiates the startup class and runs ConfigureServices(..)
  • Line 4 reaches into the ServiceCollection for the ITokenGenerator service that was setup in ConfigureServices(..)
  • Lines 5 and 6, use the TokenGenerator service to get a token a print it to screen
  • Line 7 runs the WebHost and the application starts up

Full source code here.

Dynamically Updating the Request Header of a HttpClientFactory Generated HttpClient, Part 2

Full source code here.

This is a alternative to the approach described in a previous post.

On a slack channel there was some discussion around the use of a little known extension method on HttpClientBuilder, ConfigureHttpClient. Using this extension method provides another way to dynamically alter the header of a HttpClient provided by the factory.

In ConfigureServices(..) I setup the two services I need, the MemoryCache and the TokenGenerator.

Then, where I configure the HttpClientFactory I call the ConfigureHttpClient, pass it an Action that takes the ServiceProvider and the HttpClient I’m configuring.

Inside the Action, I take a TokenGenerator from the service collection and then add the token to the client header.

public void ConfigureServices(IServiceCollection services)
{
    services.AddMemoryCache();
    services.AddSingleton<ITokenGenerator, TokenGenerator>();

    services.AddHttpClient("RemoteServer", client =>
    {
        client.BaseAddress = new Uri("http://localhost:5000/api/");
        client.DefaultRequestHeaders.Add("Accept", "application/json");
    }).ConfigureHttpClient((serviceProvider, client) =>
    {
        ITokenGenerator tokenGenerator = serviceProvider.GetService<ITokenGenerator>();
        client.DefaultRequestHeaders.Add("Token", tokenGenerator.GetToken());
    });

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}

That’s it, simpler than the approach in the previous post.

Full source code here.

How to Turn Off Console Logging for Kestrel in .NET Core

This post is mostly a note to myself.

I am often annoyed with the amount of logging to the console that occurs when I start a Kestrel hosted application and I can never remember how to turn it off.

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
    .ConfigureLogging(loggingBuilder => 
        loggingBuilder.AddFilter<ConsoleLoggerProvider>(level => 
            level == LogLevel.None))
        .UseStartup<Startup>();

Dynamically Updating the Request Header of a HttpClientFactory Generated HttpClient, Part 1

Full source code here.

There are some subtle issues in the way I use DI in this post, see here for an alternative if you don’t want to follow this approach

While using the HttpClientFactory I hit a scenario where I needed to update the value of a token passed in the header of requests, the token changed frequently, so I had to repeatedly update it throughout the lifetime of my application.

You have a couple of options for this, the first is to do it after you have taken a HttpClient from the factory at the point where you make your outbound request, this is straightforward, but now everywhere use a HttpClient you have to be able to get a new token. For some this might be fine, and you can use –

    httpClient.DefaultRequestHeaders.Add("Token", _tokenGenerator.GetToken());

Doing it with HttpClientFactory
The better approach is to put all this logic in the Startup.cs and update the header when the factory returns a new HttpClient, now everywhere you use the HttpClient gets the updated token without any work for you.

In my example case I have a token generator and memory cache. If there is a token in the cache, that one is used, if not the token generator generates and stores the new token in the cache for specified period.

In my Startup.cs all I need is this –

services.AddHttpClient("RemoteServer", client =>
{
    client.BaseAddress = new Uri("http://localhost:5000/api/");
    client.DefaultRequestHeaders.Add("Accept", "application/json");
    client.DefaultRequestHeaders.Add("Token", TokenGenerator.GetToken());
});

Read on to see how to wire everything up.

A little known feature of .NET Core is the ability to DI from Program.cs into Startup.cs, I have written about this before in Using Dependency Injection with Startup.cs in ASP.NET Core and am using it again here.

In Program.cs I add a memory cache and a token generator to the service collection.

Adding to the service collection this way can have some unexpected side effects, check this post for an alternative approach.

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
    .ConfigureServices(cs => cs.AddMemoryCache())
    .ConfigureServices(cs => cs.AddSingleton<ITokenGenerator, TokenGenerator>())
        .UseStartup<Startup>();

In Startup.cs I pass a ITokenGenerator to the constructor.

public Startup(IConfiguration configuration, ITokenGenerator tokenGenerator)
{
    Configuration = configuration;
    TokenGenerator = tokenGenerator;
    string token = tokenGenerator.GetToken(); // do something with the token
}

private ITokenGenerator TokenGenerator { get; }
// snip

Then a simple call the TokenGenerator.GetToken() updates the header of the client the factory returns to callers.

For completeness, here is the implementation of the TokenGenerator.cs

public class TokenGenerator : ITokenGenerator
{
    private readonly IMemoryCache _memoryCache;
    public TokenGenerator(IMemoryCache memoryCache)
    {
        _memoryCache = memoryCache;
    }

    public string GetToken()
    {
        string token;
        if (_memoryCache.TryGetValue("Token", out token))
        {
            return token;
        }
        else
        {
            // here you would have a more realistic way of generating a new token
            token = Guid.NewGuid().ToString();
            _memoryCache.Set("Token", token, TimeSpan.FromSeconds(10));

            return token;
        }
    }
}

Full source code here.

Loading Config from Multiple Sources with .NET Core 2.x Web Api or MVC

Full source code available here.

.NET Core 2 and .NET Core 2.1 offer many ways to load configuration and they are well documented by Microsoft. But there is one scenario that I didn’t see explained.

If you want to supplement the configuration in appsettings.json with more from a remote service, database or some other source, you first need to know where that source is, then make a request to it and add it to your configuration. You are probably going to put the location of the remote configuration appsettings.json, then you call the remote config source.
Sounds easy? It is, but not obvious.

First, a little background.

In an out of the box Web API or MVC application you get a Program.cs that looks like this –

public class Program
{
    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>();
}

On line 9, we have WebHost.CreateDefaultBuilder(args), if you peek the definition of this with Resharper you will some something like –

The highlighted code is how your appsettings.json is loaded into the configuration and added to services collection, this call happens just as you leave Program.cs

But you want to read from the appsettings.json and use a value from it to make another call to get more configuration and then add the whole lot of the services collection. You might also want to setup some logging configuration in Program.cs, so you need access to all configuration settings before calling

CreateWebHostBuilder(args).Build().Run();

Here’s how you do it.

Step 1
Inside the main method in Program.cs, build the configuration yourself –

IConfigurationBuilder builder = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json");
Configuration = builder.Build();

Get the location of the other configuration service, in my example it is another json file.
Add it to the builder and call build.

string otherConfigService = Configuration["otherConfigService"]; // this could be a database or something like consul

builder.AddJsonFile(otherConfigService);
Configuration = builder.Build();

Now you have access to the configuration values from the second source.

Configuration["SomeOtherConfigItem1"]}
Configuration["SomeOtherConfigItem2"]}

So far so good, but as mentioned above, the CreateDefaultBuilder adds the configuration to the ServiceCollection, making it available by DI. But that only happens for appsettings.json (and appsettings.{env.EnvironmentName}.json, not the config you loaded from the other source.

If you tried to access the a config setting of your from inside Startup.cs or a controller, it would not be there.

Step 2
Let’s make our configuration available via the ServicesCollection.

In the first block of code above there was a call – CreateWebHostBuilder(args).Build().Run();

I added line 4 below, this will add the configuration to the services collection.

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .UseConfiguration(Configuration); // add this line to add your configuration to the service collection

Now, the config values loaded from appsettings.json and your secondary config source will be available throughout your application.

Full source code available here.

Getting .NET Core 2.1 Preview 2 Working with Visual Studio 2017

About a year ago I wanted to start using .NET Framework 4.7, it should have been an easy process, but wasn’t. After some trial and error if figured it out and wrote a blog post explaining how to get it working.

Now with the release of .NET Core 2.1 Preview 2, I have hit the familiar problems – no obvious instructions from Microsoft, no one place to download all everything that needed and a Visual Studio install that does not include what you the latest SDK or runtime, and errors like – The specified framework 'Microsoft.AspNetCore.App', version '2.1.0-preview2-final' was not found. or, 'dotnet.exe' has exited with code -2147450730 (0x80008096).

After a few hours messing around and installing the wrong versions of the right software I figured it out.

Step 1

Install the latest version of Visual Studio Preview https://www.visualstudio.com/vs/preview/, at the time of writing this was version 15.7.0 Preview 4.0

I’m interested in developing Web API applications, so I check that box.

BUT, version 15.7.0 Preview 4.0 comes with .NET Core Version 2.1.0 Preview 1. So you don’t get the fancy new features like HttpClientFactory. Read on…

Step 2

Go to
https://github.com/dotnet/core/blob/master/release-notes/download-archives/2.1.0-preview2-download.md and download the SDK and the Runtime for your architecture.

You can verify the checksum of the downloads if you want to by comparing your SHA sum to these ones – https://dotnetcli.blob.core.windows.net/dotnet/checksums/2.1.300-preview2-008530-sdk-sha.txt

Install both the SDK and the runtime.

Step 3

To verify that that you are running .NET Core Preview 2, open visual studio and create a new .NET Core Web API application.

Once it has been created, open the .csproj file, you should see this block –

<ItemGroup>
  <PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.0-preview2-final" />
</ItemGroup>

If you don’t see Version="2.1.0-preview2-final", something has gone wrong.

You can verify what packages are installed by going to your start menu and opening Apps and Features/Add or remove programs.

If you have installed the right SDK and runtime you should see the following –