- Notifications
You must be signed in to change notification settings - Fork412
Simplified usage of BindingContext resolved command handlers#1358
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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
base:main
Are you sure you want to change the base?
Uh oh!
There was an error while loading.Please reload this page.
Conversation
I added2f2796b following thesuggestion from@leonardochaia |
leonardochaia commentedJul 21, 2021
Awesome! It looks like I would be able to remove my DI workaround with this changes. This allows to override injected dependencies for testing purposes! Can we please add this last test case? :) varparser=newCommandLineBuilder(command).ConfigureBindingContext(context=>context.AddService<IGreeter,GreeterImpl>()).ConfigureBindingContext(context=>context.AddService<IGreeter>(sp=>newGreeterMock()))// would use Moq or something here.Build();
|
@leonardochaia I don't understand what you mean? The first one will be done by this PR, the second one you could always do already? |
leonardochaia commentedJul 21, 2021
Sorry, should have been clearer. I'm just trying to be sure that the second That's why I would assert that
|
The second one thows??? Really? What exception do you get? 🤔 |
leonardochaia commentedJul 21, 2021 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
I'm sorry (again). Didn't mean to say it is throwing right now. My point is that, if the expected API is that multiple calls to So that's why I propose we add a test that represents the actual use case of overriding an already registered service. Something like EDIT: I'm not saying we should test the |
leonardochaia commentedJul 22, 2021
This looks awesome! 🎉 |
The new |
Uh oh!
There was an error while loading.Please reload this page.
| Func<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,Task<int>>action)=> | ||
| HandlerDescriptor.FromDelegate(action).GetCommandHandler(); | ||
| publicstaticICommandHandlerFromBindingContext<THandler>() |
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.
It might be helpful for the naming to reflect that the actual handler will be created during binding. One not great idea to open up discussion:DuringBindingCreateHandlerOfType<T>
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.
CreateLazyBoundHandler (to match with the existingCreate)?
Uh oh!
There was an error while loading.Please reload this page.
| }) | ||
| .ConfigureBindingContext(context=>context.AddService(_=>action)) | ||
| .ConfigureBindingContext(context=>context.AddService<IBindingContextCommandHandlerInterface,BindingContextCommandHandler1>()) | ||
| .ConfigureBindingContext(context=>context.AddService<IBindingContextCommandHandlerInterface,BindingContextCommandHandler2>()) |
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.
One concern I have with this approach is that all handler registrations for all subcommands will happen regardless of which subcommand was used, when only one subcommand is ever used. This can be a bit wasteful and is part of why we've generally steered away from the common pattern for longer-lived apps where DI is configured up front but the cost is amortized over a longer lifetime. This is very often not the case for command-line apps.
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.
@jonsequitur In this case (using the lazily bound registration) only the registration is added, no instance ofBindingContextCommandHandler1 norBindingContextCommandHandler2 is actually created at this point. And neither does the call toCommandHandler.FromBindingContext. The actual instance is first created when the command handler for the choses command is invoked.
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.
Understood. At invocation time though, all of these registrations happen on theBindingContext, and coupled with the addition of non-lazyAddService calls, this encourages the pattern I mentioned above.
Yeah, I can see that. Basically, all service registrations are technically Transient services (they all have a factory registration that is called each time the service is resolved), but of course the factory for the basic services returns singleton instances. However, the no-func overloads that I added really are Transient. Maybe add the |
I'd prefer to avoid increasing the scope of the existing DI implementation at all, which originally lacked a public A safer approach might be to allow people to bring their own |
@jonsequitur Hmm, okay, so we could just remove all the overloads to |
The way we create command handlers is undergoing some rethinking so that we can eventually remove all of the name-based matching and a lot of model binding complexity. If you're interested in the source generator approach we're looking at, take a look at this PR:#1362. |
This PR adds convenience helper methods to simplify the usage of services resolved by the BindingContext.
FromBindingContexttoCommandHandler.Creates an
ICommandHandlerwhere the specified command handler type is filled in at runtime by the BindingContext. This requires that the BindingContext knows how to instantiate the handler type (requires service registration).ConfigureBindingContextmiddleware.Middleware simply takes an
Action<BindingContext>parameter. In this callback the developer can callAddModelBinderandAddService. This simplifies service-registration on the BindingContext.AddServiceinstance method onBindingContext.Simplified service registration for trivial types. Uses a
ModelBinderto auto-construct the service.inspired by discussion in#1344.
/cc@leonardochaia