razor icon indicating copy to clipboard operation
razor copied to clipboard

Optional dependency @inject for Blazor

Open stefanloerwald opened this issue 5 years ago • 18 comments

Is your feature request related to a problem? Please describe.

In some situations, services can be optional. For regular classes we can write

public class Foo
{
    public Foo(IService service = null) {} // optional dependency on IService
}

For @inject and [Inject], there's no such equivalence.

Describe the solution you'd like

I'd love to see a parameter to the Inject attribute that allows to mark a dependency as optional, e.g. [Inject(Optional = true)]. The @inject directive could be extended to make @inject IService Service = null valid and equivalent with [Inject(Optional = true)] IService Service {get;set;} which (as far as I can see) would not break any existing code as it would be a syntax error so far.

stefanloerwald avatar Jul 22 '20 09:07 stefanloerwald

We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.

ghost avatar Jul 22 '20 16:07 ghost

Honestly, it would be nicer to be able to do "normal" constructor injection using codebehind mycomponent.razor.cs classes. Then you can have optional injection if you need it without faffing around with @ directives.

richbryant avatar Oct 07 '20 08:10 richbryant

Honestly, I don't mind which way it works, as long as I can make dependencies optional: whether allowing DI constructors or adding support for optional dependencies with directives has the smaller impact on the framework, I can't say.

stefanloerwald avatar Oct 07 '20 09:10 stefanloerwald

let's just say, parameter injection is nasty. And the @ directive is really just parameter injection in disguise. I'm not saying do away with it, some people like code and markup in the same file and who am I to judge? But where you have a complex requirement, there's a real benefit to going down the standard path.

richbryant avatar Oct 07 '20 09:10 richbryant

@stefanloerwald:

meanwhile, can you use this?

@inject IServiceProvider ServiceProvider

@code 
{
    IService _service;

    protected override void OnInitialized() 
    {
        _service = ServiceProvider.GetService<IService>();
    }
}

Liero avatar Apr 06 '21 13:04 Liero

Thank you @Liero for the suggestion. That code absolutely works and is a nice workaround. I still hope the feature will be added directly into Blazor.

stefanloerwald avatar Apr 06 '21 14:04 stefanloerwald

Any update on this?

andersson09 avatar Apr 01 '22 16:04 andersson09

Thanks for contacting us.

We're moving this issue to the .NET 8 Planning milestone for future evaluation / consideration. We would like to keep this around to collect more feedback, which can help us with prioritizing this work. We will re-evaluate this issue, during our next planning meeting(s). If we later determine, that the issue has no community involvement, or it's very rare and low-impact issue, we will close it - so that the team can focus on more important and high impact issues. To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.

ghost avatar Sep 28 '22 17:09 ghost

I would also appreciate this. One use case where it makes sense: Imagine you are developing a reusable Blazor component. Now you want to inject IStringLocalizer<> in case the app support multilang. But if IStringLocalizer<> is not registered as a service, it is safe to assume the app is single language and should display all messages in a default language. Right now there is no clean way to support both scenarios, as injecting IStringLocalizer will throw an error saying the service is not registered.

MarcoTheFirst avatar Dec 20 '22 15:12 MarcoTheFirst

I also feel this would be a useful feature. There are times I want to abstract a component out and allow the user to pass their own handler but I don't want to make them specify one every time and I don't want them to have to register a service for it so this feature would allow for a default one to be specified.

Aeroverra avatar Jan 10 '23 01:01 Aeroverra

Another use case we've found for it:

We have a custom validation component. Currently it uses DI to get the correct validator. We'd like to make it so that the user can provide their own validator in certain cases. To make it work as things currently are, we need to add a nullable validator property and then default back to DI if it's null or not and we need to babysit the property and make sure that our real validator is updated whenever the property changes.

UniMichael avatar Feb 15 '24 19:02 UniMichael

This would also be very useful for prerendering. I've got a scenario where I have a service which does javascript interop, but on the server it doesn't do anything. I currently have to write the following to make prerendering work:

public interface IMyService
{
    ValueTask DoSomething();
}

public class MyService : IMyService
{
    public ValueTask DoSomething()
    {
        // Do something
    }
}

public class MyVoidService : IMyService 
{
     public ValueTask DoSomething() => ValueTask.CompletedTask;
}

Having optional DI would solve the problem of having to write services just to eat the errors

StefanJanssen95 avatar Feb 22 '24 16:02 StefanJanssen95

Thanks for contacting us.

We're moving this issue to the .NET 8 Planning milestone for future evaluation / consideration. We would like to keep this around to collect more feedback, which can help us with prioritizing this work. We will re-evaluate this issue, during our next planning meeting(s). If we later determine, that the issue has no community involvement, or it's very rare and low-impact issue, we will close it - so that the team can focus on more important and high impact issues. To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.

Can this be moved into .NET 9 planning, or would it be best to place into .NET 10 planning at this point?

thirstyape avatar Feb 27 '24 18:02 thirstyape

@StefanJanssen95

This would also be very useful for prerendering. I've got a scenario where I have a service which does javascript interop, but on the server it doesn't do anything. Having optional DI would solve the problem of having to write services just to eat the errors

You don't have to write dummy service, see https://github.com/dotnet/razor/issues/7653#issuecomment-1295644712

Liero avatar Feb 28 '24 14:02 Liero

@danroth27,

This looks like a great new feature. Would it be possible to do something such as the following to make injection optional?

public partial class ConstructorInjection(NavigationManager? navigation = null)
{
}

thirstyape avatar Mar 29 '24 23:03 thirstyape

This looks like a great new feature. Would it be possible to do something such as the following to make injection optional?

public partial class ConstructorInjection(NavigationManager? navigation = null)
{
}

@thirstyape Yup, exactly. You can use the new constructor injection support to make injected services optional.

danroth27 avatar Mar 31 '24 03:03 danroth27

This looks like a great new feature. Would it be possible to do something such as the following to make injection optional? public partial class ConstructorInjection(NavigationManager? navigation = null) { }

@thirstyape Yup, exactly. You can use the new constructor injection support to make injected services optional.

I am not exactly sure, but I think NavigationManager? navigation = null is not working because the type is not and cannot be registered as nullable type in DI container. I have tried this approach in my application and the parameter is always null. 🤔

ViRuSTriNiTy avatar Nov 06 '25 17:11 ViRuSTriNiTy