-
Notifications
You must be signed in to change notification settings - Fork 10.4k
Added ControllerRequestDelegateFactory #27773
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
src/Mvc/Mvc.Core/src/DependencyInjection/MvcCoreServiceCollectionExtensions.cs
Show resolved
Hide resolved
- Today there's lots of indirection to figure out which invoker is responsible for executing a particular request based on the `ActionContext`. The `IActionInvokerFactory` calls through `IActionInvokerProviders` and an `IActionInvoker` is picked as a result of this. The default implementations based this decision on the type of action descriptor (though that's not required) which means its possible to avoid this per request indirection and select the invoker based on the descriptor. The `ControllerRequestDelegateFactory` creates a `RequestDelegate` for the endpoint don't go through indirection to find the ControllerActionInvoker. PS: This code path breaks extensibility for IActionInvoker* types that want to take over invocation of existing ControllerActionDescriptor since it no longer calls them in the endpoint routing code path. This may warrant a compatibility switch
29ebb0c
to
e1bedc9
Compare
- This will allow people to turn off the request delegate factory if they were using that extensiblity point before.
Thank you for submitting this for API review. This will be reviewed by @dotnet/aspnet-api-review at the next meeting of the ASP.NET Core API Review group. Please ensure you take a look at the API review process documentation and ensure that:
|
FWIW - I'm not aware of anyone using this for extensiblity for anything real (along with filter providers). There were some projects using it back when our implementation of the invoker was public. Now that this requires some really complicated work, it's just not realistic for someone to extend. |
|
||
controllerContext.ModelState.MaxAllowedErrors = _maxModelValidationErrors; | ||
|
||
var (cacheEntry, filters) = _controllerActionInvokerCache.GetCachedResult(controllerContext); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another thing to investigate. This is a cache lookup that hits a concurrent dictionary (keyed on ActionDescriptor). Since you're creating a request delegate per-action there's no need for additional caches and all that stuff can get closed over here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
||
namespace Microsoft.AspNetCore.Mvc.Routing | ||
{ | ||
internal class ControllerRequestDelegateFactory : IRequestDelegateFactory |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you tried implementing this yet for Pages? I suspect it's more complex
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have a stash for pages and it is more complex.
Hello @davidfowl! Because this pull request has the p.s. you can customize the way I help with merging this pull request, such as holding this pull request until a specific person approves. Simply @mention me (
|
/azp run |
Azure Pipelines successfully started running 2 pipeline(s). |
You said you were close to having an IRequestDelegateFactory implementation ready for Razor Pages. Can that be included in this PR? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After some offline discussion, I'm fine with looking at the Razor Page change in a separate PR.
FWIW, here are the benchmark results for an early version of this PR hitting the following action using this crank config: public class Person
{
public string Name { get; set; }
}
[ApiController]
public class EchoController : ControllerBase
{
[HttpPost("/EchoController")]
public Person Post(Person person) => person;
A ~7.5% RPS improvement for this action with a small JSON payload. |
@davidfowl pointed out what I measured before removed a lot more stuff than this PR ultimately did. Here are the numbers for the actual PR:
It looks like there might be a small perf improvement, but it's within the margin of error. |
ActionContext
. TheIActionInvokerFactory
calls throughIActionInvokerProviders
and anIActionInvoker
is picked as a result of this. The default implementations based this decision on the type of action descriptor (though that's not required) which means its possible to avoid this per request indirection and select the invoker based on the descriptor. TheControllerRequestDelegateFactory
creates aRequestDelegate
for the endpoint don't go through indirection to find the ControllerActionInvoker.API