YairHalberstadt / stronginject

compile time dependency injection for .NET
MIT License
845 stars 24 forks source link

Support for [FromServices] attribute? #112

Closed zejji closed 3 years ago

zejji commented 3 years ago

I have started using StrongInject and love the idea of compile-time DI, but have not been able to get things to work with the [FromServices] attribute.

Specifically I get the following error when trying to inject a service into a controller action using [FromServices], which I presume means that the service is not being resolved by StrongInject at all:

System.InvalidOperationException: No service for type 'StrongInjectTest.Services.IMyTestService' has been registered.
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
   at Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ServicesModelBinder.BindModelAsync(ModelBindingContext bindingContext)
   at Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder.BindModelAsync(ActionContext actionContext, IModelBinder modelBinder, IValueProvider valueProvider, ParameterDescriptor parameter, ModelMetadata metadata, Object value, Object container)
   at Microsoft.AspNetCore.Mvc.Controllers.ControllerBinderDelegateProvider.<>c__DisplayClass0_0.<<CreateBinderDelegate>g__Bind|0>d.MoveNext()

I find the [FromServices] pattern to be very useful to avoid creating too many services in the constructor when they are only used in a single controller action.

Is this currently possible and, if not, is it something which is planned for the future?

YairHalberstadt commented 3 years ago

Hi

I'm glad you like the idea of compile-time DI!

The FromServices attribute is deeply linked into the Asp.Net model - it tells Asp.Net to look at the IServiceProvider when getting the parameter. As such it is possible to use in conjunction with StrongInject, but it won't work nicely and StrongInject can't provide too much in the way of compile time checking.

For example you could directly register IMyTestService with the IServiceCollection. Alternatively you could create an IContainer<IMyTestService> and register that with the IServiceCollection using AddTransientServiceUsingContainer.

As a general principle, StrongInject assumes that all dependencies are injected through the constructor. Whilst I can see the benefits of doing this some other way, I think this remains best practice, and will likelier get easier once (if) C# introduces primary constructors. In general you'll find things easier if you go with the 'standard' way of doing things with StrongInject, instead of using some other technique. I know this can be frustrating, but I've found it a good principle for all frameworks I've worked with!

If this is very important to enough people, it would be possible to create an analyzer which detected when FromServices was used and warned if you didn't register that with the IServiceCollection, but I'm not sure this would actually be a good idea as it could have many false negatives.