Daily Knowledge Drop
When using minimal apis in .NET6, a class can be injected into the relevant endpoint handler method, without explicitly specifying where the instance is coming from
- it is determined by the runtime (and an error thrown if it could not determined).
However when using controllers, and a class is being injected, it has to explicably be stated
where the instance is coming from.
This changes in .NET7, where the controller behavior is changing to be more in-line with the minimal api behavior.
Setup
In the below example, we have a RandomNumberGenerator class, with one method which returns a random number:
public class RandomNumberGenerator
{
public int GetRandomNumber(int max)
{
var generator = new Random();
return generator.Next(max);
}
}
In both examples below, this class is added to the dependency injection container as a singleton:
builder.Services.AddSingleton<RandomNumberGenerator>();
Minimal api
In the minimal api, the endpoint pattern is specified, as well as the lambda handler - any number of parameters can be passed to the handler. In the below case, an instance of RandomNumberGenerator is passed in, a random number between 0 and 100 generated, and then returned:
app.MapGet("/randomnumber", (RandomNumberGenerator generator) =>
{
return generator.GetRandomNumber(100);
})
.WithName("GetRandomNumber");
In the above example, the runtime can determine that a service of type RandomNumberGenerator
has been registered with the dependency injection container, so this is most likely what needs to be injected into the endpoint handler.
Controller api
Before .NET 7
If we look at the same setup as above with the minimal api, but this time using controllers:
[ApiController]
[Route("[controller]")]
public class RandomNumberController : ControllerBase
{
[HttpGet(Name = "RandomNumber")]
public int Get(RandomNumberGenerator generator)
{
return generator.GetRandomNumber(100);
}
}
However, when executing this, the following error is received:
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.13",
"title": "Unsupported Media Type",
"status": 415,
"traceId": "00-fd8fe900bf366defa058b51f685d11f1-896d209554184932-00"
}
This occurs because when using controllers before .NET7, if a parameter is coming from the dependency injection container, it has to explicably be stated
using the FromServices
attribute.
[ApiController]
[Route("[controller]")]
public class RandomNumberController : ControllerBase
{
[HttpGet(Name = "RandomNumber")]
public int Get([FromServices]RandomNumberGenerator generator)
{
return generator.GetRandomNumber(100);
}
}
With this attribute added on line 6, the controller endpoint can now be called successfully with a random number successfully being returned.
.NET 7
This changes with .NET7 - when using controllers, the FromServices
attribute can now also be (optionally) omitted. The following will work:
[ApiController]
[Route("[controller]")]
public class RandomNumberController : ControllerBase
{
[HttpGet(Name = "RandomNumber")]
public int Get(RandomNumberGenerator generator)
{
return generator.GetRandomNumber(100);
}
}
Notes
This is a relatively minor change, but a welcome one - more closely aligning the behavior of minimal apis and controllers.
Daily Drop 73: 13-05-2022
At the start of 2022 I set myself the goal of learning one new coding related piece of knowledge a day.
It could be anything - some.NET / C# functionality I wasn't aware of, a design practice, a cool new coding technique, or just something I find interesting. It could be something I knew at one point but had forgotten, or something completely new, which I may or may never actually use.
The Daily Drop is a record of these pieces of knowledge - writing about and summarizing them helps re-enforce the information for myself, as well as potentially helps others learn something new as well.On This Page