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>();

HttpContent ReadAsAsync with .NET Core 2

Full source code available here.

If you are used to using HttpContent.ReadAsAsync you might be surprised to learn that it is missing from .NET Core 2. You can try adding Microsoft.AspNet.WebApi.Client but you might get warnings or errors.

At some point Microsoft will come out with an updated NuGet package, but in the meantime here is a work around.

At this extension method to your code.

using Newtonsoft.Json;
using System.Net.Http;
using System.Threading.Tasks;

namespace ReadAsAsyncCore
{
    public static class HttpContentExtensions
    {
        public static async Task<T> ReadAsJsonAsync<T>(this HttpContent content)
        {
            string json = await content.ReadAsStringAsync();
            T value = JsonConvert.DeserializeObject<T>(json);
            return value;
        }
    }
}

And use like this.

HttpResponseMessage httpResponse = await httpClient.GetAsync(requestEndpoint);
List<Product> products = await httpResponse.Content.ReadAsJsonAsync<List<Product>>();

Full source code available here.

.Net Core Multiple Get Methods with the Action Method Selector Attribute

Full source code available here.

In .Net Core Web Api it is not possible for the routing mechanism to distinguish between the following action methods.

public string GetSomething(int id, int something)
and
public string GetAnother(int id, int another)

But with the help of an ActionMethodSelectorAttribute we can tell the methods apart.

Step 1
Add a new class called QueryStringConstraint and inherit from ActionMethodSelectorAttribute.

public class QueryStringConstraint : ActionMethodSelectorAttribute
{
	public override bool IsValidForRequest(RouteContext routeContext, ActionDescriptor action)
	{
		IList<ParameterDescriptor> methodParameters = action.Parameters;

		ICollection<string> queryStringKeys = routeContext.HttpContext.Request.Query.Keys.Select(q => q.ToLower()).ToList();
		IList<string> methodParamNames = methodParameters.Select(mp => mp.Name.ToLower()).ToList();

		foreach (var methodParameter in methodParameters)
		{
			if (methodParameter.ParameterType.Name.Contains("Nullable")) 
			{
				//check if the query string has a parameter that is not in the method params
				foreach(var q in queryStringKeys)
				{
					if (methodParamNames.All(mp => mp != q))
					{
						return false;
					}
				}

				if(queryStringKeys.All(q => q != methodParameter.Name.ToLower()))
				{
					continue;
				}
			}
			else if (queryStringKeys.All(q => q != methodParameter.Name.ToLower()))
			{
				return false;
			}
		}
		return true;
	}
}

Step 2
Add the QueryStringConstraint to the action methods that need to be distinguished by query string parameters.

[QueryStringConstraint] 
public string GetAnother(int id, int another)
{
	return $"GetAnother {id} {another}";
}

// http://localhost:62922/api/values/?id=1&something=22
[QueryStringConstraint] 
public string GetSomething(int id, int something)
{
	return $"GetSomething {id} {something}";
} 

Full source code available here.

.NET Core Web Api Routing

Full source code available here.

Routing in .NET Core Web Api (1.1 and 2) is a little different than in earlier versions.

I’ve written about this a few times, you can find those posts here.

In this post I’m going to show some examples of routing that might help you if you are having problems, especially if you want to have multiple GET methods. In a later article I will show how to use the action method selector to choose between actions methods with the same signature.

The code and comments are fairly self explanatory.

[HttpGet("api/Person")]
public class PersonController : Controller
{
	// GET: api/Person
	[HttpGet]
	public IEnumerable<string> Get()
	{
		return new string[] { "Dave Smith", "Edward Temple" };
	}

	// http://localhost:27624/api/person/1
	[HttpGet("{id}")]
	public string Get(int id)
	{
		return $"Get by id:{id} - Dave Smith";
	}

	// http://localhost:62689/api/person/ByNumber/5
	[HttpGet("ByNumber/{number}")]
	public string GetByNumber(int number)
	{
		return $"Get by number:{number} - Tom Davis";
	}
	// http://localhost:62689/api/person/ByFirstName/Steve
	[HttpGet("ByFirstName")]
	public string GetByFristName(string firstName)
	{
		return $"Get by first name - {firstName} Smith";
	}

	// http://localhost:62689/api/person/ByLastName?lastname=Smith
	[HttpGet("ByLastName")]
	public string GetByLastName(string lastName)
	{
		return $"Get by last name - Dave {lastName}";
	}

	// http://localhost:62689/api/person/ByFirstAndLastName?firstname=Steve&lastname=Smith
	[HttpGet("ByFirstAndLastName")]
	public string GetByFirstAndLastName(string firstName, string lastName)
	{
		return $"Get by first and last name - {firstName} {lastName}";
	}
}

Here are some more examples with attribute routing and a query model.

[HttpGet("api/accounts/{AccountId}")]
public class AccountsController : Controller
{
	// http://localhost:62689/api/accounts/11
	public IActionResult Get(int accountId)
	{
		return Ok(accountId);
	}

	// http://localhost:62689/api/accounts/?accountId=11
	// http://localhost:62689/api/accounts/?accountId=11&AccountName=dave
	// http://localhost:62689/api/accounts/?accountId=11&AccountName=dave&managerid=15
	[HttpGet("/api/accounts/")]
	public IActionResult Get(QueryModel queryModel)
	{
		return Ok(queryModel);
	}

	// http://localhost:62689/api/accounts/11/manager/22
	[HttpGet("Manager/{ManagerId}")]
	public IActionResult Get(int accountId, int managerId)
	{
		return Ok($"accountId:{accountId}, managerId:{managerId}");
	}

	// http://localhost:62689/api/accounts/11/manager/22/role/33
	[HttpGet("/api/accounts/{AccountId}/Manager/{ManagerId}/Role/{RoleId}")]
	public IActionResult Get(int accountId, int managerId, int roleId)
	{
		return Ok($"accountId:{accountId}, managerId:{managerId}, roleId:{roleId}");
	}
}

public class QueryModel
{
	public int AccountId { get; set; }
	public string AccountName { get; set; }
	public int ManagerId { get; set; }
}

Full source code available here.

Fluent Validation in ASP.NET Core

Full source code available here.

I have written about Fluent Validation a couple of times. There is a new library available for .Net Core.

How to return to validation messages back to the caller is not immediately obvious. You might see the validators running, but the responses are missing! Here is how to solve that problem.

Step 1

Firstly add the package to your Web Api project.
Install-Package FluentValidation.AspNetCore.
If you are going to keep your validators in the Web Api project that is all you need to add.

But if you put the validators in a different project you need to add the FluentValidation package to that projecty
Install-Package FluentValidation.

Step 2

In startup.cs add –

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(options =>
        {
            options.Filters.Add(typeof(ValidateModelAttribute));
        })
        .AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining<PersonModelValidator>());
}

Note the line options.Filters.Add(typeof(ValidateModelAttribute)), we need to add that filter.

Step 3

Add the validation filter –

public class ValidateModelAttribute : ActionFilterAttribute
{	
    public override void OnActionExecuting(ActionExecutingContext context)	
    {	
        if (!context.ModelState.IsValid)	
        {	
            context.Result = new BadRequestObjectResult(context.ModelState);
        }
    }
}
Step 4

Add a model and a validator.

public class PersonModel
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class PersonModelValidator : AbstractValidator<PersonModel>
{
    public PersonModelValidator()
    {
        RuleFor(p => p.FirstName).NotEmpty();
        RuleFor(p => p.LastName).Length(5);
    }
}

Full source code available here.