2

I would like to register in- and outputformatters in DI, however I don't know how to get to the DI container in the AddControllers method:

var builder = WebApplication.CreateBuilder(args);

builder.Services
    .AddTransient<TexInputFormatter, MyInputFormatter>()
    .AddTransient<TextOutputFormatter, MyOutputFormatter>()
    .AddControllers(c =>
    {
        c.InputFormatters.Clear();
        c.OutputFormatters.Clear();
        
        // How do I get to the DI container here?
        c.InputFormatters.Add(???.GetRequiredService<TextInputFormatter>());
        c.OutputFormatters.Add(???.GetRequiredService<TextOutputFormatter>());
    });

var app = builder.Build();
app.Run();

I did come up with one 'workaround' by declaring app earlier and the capturing it, but I don't like that solution:

WebApplication? app = null;  // Declare app here
var builder = WebApplication.CreateBuilder(args);

builder.Services
    .AddTransient<TexInputFormatter, MyInputFormatter>()
    .AddTransient<TextOutputFormatter, MyOutputFormatter>()
    .AddControllers(c =>
    {
        c.InputFormatters.Clear();
        c.OutputFormatters.Clear();
        
        // Use app.Services here
        c.InputFormatters.Add(app!.Services.GetRequiredService<TextInputFormatter>());
        c.OutputFormatters.Add(app!.Services.GetRequiredService<TextOutputFormatter>());
    });

app = builder.Build();
app.Run();

Edit: Ofcourse I could do:

c.InputFormatters.Add(new MyInputFormatter());
c.OutputFormatters.Add(new MyOutputFormatter());

However, both formatters have a bunch of other dependencies and constructor arguments I want DI to resolve for me.

How would I go about this?

Steven
  • 166,672
  • 24
  • 332
  • 435
RobIII
  • 8,488
  • 2
  • 43
  • 93
  • 1
    @PeterCsala That won't work because a) the servicecollection isn't built yet and b) the builder.Services doesn't have a GetRequiredService method. – RobIII Jul 15 '22 at 09:23
  • 1
    How about this? https://andrewlock.net/accessing-services-when-configuring-mvcoptions-in-asp-net-core/ – DavidG Jul 15 '22 at 09:28
  • from Microsoft [documents](https://learn.microsoft.com/en-us/aspnet/core/web-api/advanced/custom-formatters?view=aspnetcore-6.0#specify-supported-media-types-and-encodings), it is clearly given that you can't use constructor DI but you can use context info and resolve the dependency whenever needed. there is also [sample](https://github.com/dotnet/AspNetCore.Docs/blob/main/aspnetcore/web-api/advanced/custom-formatters/samples/6.x/CustomFormattersSample/Formatters/VcardInputFormatter.cs) available on how to do it. – CodingMytra Jul 15 '22 at 10:13
  • @CodingMytra Where is 't "clearly given" that I can't use constructor DI? What prevents me to make a `MyOutputFormatter(IOptions options, ILogger<...> logger, ISomething something)`? As a matter of fact, it works just fine as long as I can get to the DI container. Yes, I can use the context but that would mean accessing the DI container on every input/output where my formatters are perfectly fine as a Singleton (if so desired). I would like to perform some (a little) work in the constructor, but prefer to do it just once. – RobIII Jul 15 '22 at 11:57
  • 1
    Using the context is almost certainly the best idea. If you are afraid of performance issues, then save those things you extract from the service provider in static variables. – DavidG Jul 15 '22 at 12:09
  • @DavidG Yeah, I guess so. I'll go for the context (for now ) – RobIII Jul 15 '22 at 12:23
  • "A formatter class can not use constructor injection for its dependencies". this line is written in the document link I shared. – CodingMytra Jul 15 '22 at 12:23
  • @DavidG If you post your comment as an answer I will accept it. – RobIII Jul 21 '22 at 23:21

2 Answers2

0

You need a IServiceProvider to get something from your ServiceContainer. To do that you need to build it.

You could use something like that

builder.Services.BuildServiceProvider();

However i read somewhere that this is not the right way to do it (You should not build the provider twice).

Related: For services you could do:

builder.Services.AddSingletion<IFoo, MyService>(serviceProvider => serviceProvider.GetRequiredService<IFoo2>();...);
David
  • 90
  • 9
  • Yes, that's also not a preferred option. Maybe it works but it's not recommended. – RobIII Jul 15 '22 at 09:22
  • 3
    See [this q&a](https://stackoverflow.com/questions/56042989/what-are-the-costs-and-possible-side-effects-of-calling-buildserviceprovider-i/56058498#56058498) on why building extra service providers is typically a bad idea. – Steven Jul 15 '22 at 10:49
  • @Steven Thanks, that was exactly what i was refering to – David Jul 15 '22 at 11:13
-1

You don't need DI for adding input or output formatters. just adding new instance should be fine.

check this article and this one for more details:

var builder = WebApplication.CreateBuilder(args);

builder.Services
    .AddControllers(c =>
    {
        c.InputFormatters.Clear();
        c.OutputFormatters.Clear();
        
        // How do I get to the DI container here?
        c.InputFormatters.Add(new MyInputFormatter());
        c.OutputFormatters.Add(new MyOutputFormatter());
    });

var app = builder.Build();
app.Run();
CodingMytra
  • 2,234
  • 1
  • 9
  • 21
  • I know, but my in- and outputformatters each have a bunch of constructor arguments that I would also like DI to resolve for me. I'll add that to my question. (And just so we're clear, the downvote isn't mine...) – RobIII Jul 15 '22 at 09:21
  • that's fine, it was not clear from your que that you needed additional dependencies. – CodingMytra Jul 15 '22 at 09:39