question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Add support for implicit inference of `FromServices` for types that appear in DI

See original GitHub issue

Current design

With the Minimal APIs, what the RequestDelegateFactory does is fall back to try check if the Parameter is a Service registered in the DI when:

  1. No Metadata attributes set and
  2. No special types (HttpContext, HttpRequest, HttpResponse, ClaimsPrincipal, CancellationToken, IFormFileCollection and IFormFile) and
  3. Parameter does not have BindAsync method and
  4. Parameter is not string and
  5. Parameter does not have TryParse method

Currently MVC do not have support for items 3 and 4.

The way MVC implements the ModelBinding logic is through ModelBinders that will be assigned, by Provider (Eg. ComplexObjectModelBinderProvider) based on the Parameter metadata.

Also, the binding FromServices is already implemented (ServicesModelBinderProvider/ServicesModelBinder) that will be used when the BindingSource is set to Services.

For API Controllers, the BindingSource will be inferred, when the metadata is not set yet, based on with the following logic:

  1. Complex Type => BindingSource = Body
  2. Is part of any route => BindingSource = Path
  3. Default => BindingSource = Query

In addition to that, Item 2 is similar in MVC since the Special or FormFile binding source is set by BindingSourceMetadataProvider, except for HttpContext, HttpRequest and HttpResponse that are available in the ControllerBase class

Eg.:

modelMetadataDetailsProviders.Add(new BindingSourceMetadataProvider(typeof(CancellationToken), BindingSource.Special));
modelMetadataDetailsProviders.Add(new BindingSourceMetadataProvider(typeof(IFormFile), BindingSource.FormFile));
modelMetadataDetailsProviders.Add(new BindingSourceMetadataProvider(typeof(IFormCollection), BindingSource.FormFile));
modelMetadataDetailsProviders.Add(new BindingSourceMetadataProvider(typeof(IFormFileCollection), BindingSource.FormFile));
modelMetadataDetailsProviders.Add(new BindingSourceMetadataProvider(typeof(IEnumerable<IFormFile>), BindingSource.FormFile));

Based on the previous logic, nothing will be inferred as Services and the only way to the BindingSource to be set to Services is using the [FromServices] attribute.

Proposed Change

Update the Infer mechanism to do the following:

https://github.com/dotnet/aspnetcore/blob/8bd8f58516c4954dce14944844d3a678d5b52789/src/Mvc/Mvc.Core/src/ApplicationModels/InferParameterBindingInfoConvention.cs#L94

  1. Complex Type: a. Is Registered in the DI =>BindingSource = Services b. No registered => BindingSource = Body
  2. Simple Types: a. Is part of any route => BindingSource = Path
  3. Default => BindingSource = Query

My proposal is to use the IServiceProviderIsService service, same used by the RequestDelegateFactory, injected in the constructor.

    public InferParameterBindingInfoConvention(
        IModelMetadataProvider modelMetadataProvider,
+         IServiceProviderIsService? serviceProviderIsService = null)

And update the InferBindingSourceForParameter method to verify if the parameter type is registered in the DI.

          if (_serviceProviderIsService.IsService(parameter.ParameterType))
          {
               return BindingSource.Services;
          }

That will cover the idea of implicit inference of FromService since the ServiceModelBinder will be activated when we set the BindingSource to Services.

Also, I prefer this to be the new default behavior, so my suggestion is to include allowing users to opt-out:

namespace Microsoft.AspNetCore.Mvc;
public class ApiBehaviorOptions
{
+    public bool SuppressInferBindingFromServicesForParameters { get; set; }
}

Usage Examples

services.Configure<ApiBehaviorOptions>(options => {
     options.SuppressInferBindingFromServicesForParameters = true;            
});

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:9 (9 by maintainers)

github_iconTop GitHub Comments

1reaction
halter73commented, Jan 27, 2022

We just approved DisableImplicitFromServiceParameters for the same option in SignalR. https://github.com/dotnet/aspnetcore/pull/34047 It’s not too late to change the property name in SignalR, but I think we should use the same name.

0reactions
brunolins16commented, Feb 8, 2022
Read more comments on GitHub >

github_iconTop Results From Across the Web

API controller actions try to infer parameters from DI - .NET
Types in DI are checked at app startup using IServiceProviderIsService to determine if an argument in an API controller action comes from DI...
Read more >
ASP.NET Core updates in .NET 7 Preview 2
Infer API controller action parameters that come from services ... methods now support injecting services through dependency injection (DI).
Read more >
.NET 7: Microsoft Reveals New ASP.NET Core Features
Let's take a look at what's new in ASP.NET Core in .NET 7! Infer service-sourced API controller action parameters. Previously it was necessary ......
Read more >
How to use parameter binding in minimal APIs in ASP.NET ...
Take advantage of parameter binding in ASP.NET Core 7 to convert request data to strongly typed parameters, improving both application ...
Read more >
How to use the FromServices attribute in ASP.NET Core
Take advantage of the FromServices attribute in ASP.NET Core to inject dependencies directly into your action methods.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found