-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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
Reduce constructor injection in controllers when possible #15473
Conversation
{ | ||
private readonly IAuthorizationService _authorizationService; | ||
private readonly IAdminDashboardService _adminDashboardService; | ||
private readonly IContentManager _contentManager; | ||
private readonly IContentItemDisplayManager _contentItemDisplayManager; | ||
private readonly IContentDefinitionManager _contentDefinitionManager; | ||
private readonly IUpdateModelAccessor _updateModelAccessor; |
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.
lolz, this was originally done to make it replacable (from memory), so suspect you are breaking something for someone 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.
If this was the case, it would be replaced in a test case. I don't think we are replacing it from a test so it should be fine.
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.
haha. you're dreaming if you think the tests cover everything :)
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.
That's not what I meant. We have a filter that changes the modelBinderAccessor.ModelUpdater
used on the controller which reads it from the services collection. So if someone want to provider their own implementation of IUpdateModelAccessor
the updated code will read their custom implementation and use. So it should not break anyone.
OrchardCore/src/OrchardCore/OrchardCore.DisplayManagement/ModelBinding/ModelBinderAccessorFilter.cs
Lines 19 to 20 in d99deaa
var modelBinderAccessor = context.HttpContext.RequestServices.GetRequiredService<IUpdateModelAccessor>(); | |
modelBinderAccessor.ModelUpdater = new ControllerModelUpdater(controller); |
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.
@deanmarcussen do you still think there is an issue with this?
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.
Where resolved, do you mean? In the example of this controller, IContentItemDisplayManager
and the drivers down the line will use the instance of the controller as IUpdateModel
. You could previously override that via IUpdateModelAccessor.ModelUpdater
but not anymore.
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.
@Piedone look at the comment above #15473 (comment)
The same instance is already set at every controller from the filter. So if you resolve that updater directly from the controller or you resolver it from the container, you'll get the same instance. At the end of the day, they all resolve the instance that you registered in the container.
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.
On line 75 the controller instance is passed to _contentItemDisplayManager.BuildDisplayAsync()
. Due to this, it doesn't matter what's in IUpdateModelAccessor.ModelUpdater
, display management will use the controller.
Before this change, the (current) controller being used as IUpdateModel
was one option that was the default, set by the filter you mention. Now it's the only option, and even if you set ``IUpdateModelAccessor.ModelUpdater` to something else, still, the controller will be used. That's why I'm saying it's a capability lost (whether it's a useful capability is a different question, and I'm not sure).
Since almost all of the cases where the current IUpdateModel
instance is accessed are in drivers, and thus coming similarly from controllers (there are a handful of cases of IUpdateModelAccessor
being used in services and views), this PR effectively removes the ability to use anything else than the current controller for IUpdateModel
.
Again, I'm not sure if this is a bad thing, but it's an impactful one.
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.
Looking at this more, there is no way to override the update logic that asp.net core. So the idea of changing the implementation is not ideal because even if you do, you can't change the updater behavior that is in the BasecController. I am wondering if we really need to care about allowing others to change the behavior of IUpdateModel since that will only change the logic partially "not for the methods found in the controller".
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.
@sebastienros thought here? Do we really need or should care about injecting IUpdateModelAccessor
into controllers where there is no pure way to change the updater behavior in the app entirely even when we want to?
What's the goal of this change? Less code? Performance? |
@Piedone the idea is to reduce the amount of services needed on every single request in these controllers. Also, we can clean up the code by reducing the amount of services needed in the constructor. |
Yeah but what's the goal with reducing the services, performance? Because perhaps the injection itself (i.e. passing the object to the ctor) has some measurable performance impact, but the resolution is most certainly happening anyway, because something will use |
For the IUpdateModelAccessor it's just at resolution time because that service will always be constructed in the filter. But the other services it helps reducing the need of creating the services at the controller service unless needed later on by other services. |
|
Yes there will be no construction needed because it is scoped "and constructed earlier". But there will be a resolution as the container has to get the instance of the service from the container and then inject it again in the controller. Yes it is a very cheap operation, but it is an operation that has to be done. Anyway, if we find benefit of keeping that service in the controller, we can keep it "cheap operation". But why would someone need that in OC controller? If someone uses OC and want to provide their own implementation, they should be able to do it even after this change. They can register their own implementation and we'll resolve their custom implementation and use it in our controllers. I don't see a good reason to inject it in the controller's constructor. On the other hand, if we were to build a test case in OC for a controller, we can Mock the filter and inject anything we want. But even if we don't want to mock the filter, we can then inject the service in the controller. But we should just inject it just because since we don't have a test case that replace that implementation. |
We need to figure that out under #15473 (comment). |
This pull request has merge conflicts. Please resolve those before requesting a review. |
This pull request has merge conflicts. Please resolve those before requesting a review. |
No description provided.