Aspnetcore: InjectAttribute outside of components

Created on 29 Mar 2018  路  10Comments  路  Source: dotnet/aspnetcore

I am missing a mechanism in Blazor's dependency injection. If it already exists and I just missed it in Blazor's code, please let me know. If not, here is my suggestion as a user story:

As a Blazor programmer, I would like to use the InjectAttribute in classes other than BlazorComponent, too.

Example in pseudo-code:

class DataAccess : IDataAccess
{
  [Inject] <<-- This is what I am missing, member not filled by Blazor's DI
  HttpClient Http { get; set; }
  ...
  void DoSomething()
  {
    // Here I would like to use this.Http
  }
}

@page "/"
@inject IDataAccess DataAccess
...

Use case:
I could implement a class DataAccess that encapsulates all web API calls for a Blazor application. Blazor components receive an instance of this class using the InjectAttribute. However, DataAccess needs HttpClient and should get it using dependency injection. As far as I have seen from Blazor's code and based on some tests I did, the class DataAccess would not receive any value from Blazor's DI system.

area-blazor

Most helpful comment

Also to be clear, the primary reason why InjectAttribute exists is just to support the Razor @inject syntax.

We're not trying to stop developers from using [Inject] directly, but for Razor-based components, it should never be necessary. It's much nicer to use @inject TypeName PropertyName instead. People should rarely see [Inject] explicitly in their own code.

I'm guessing you're using it currently in a base class because of the lack of partial support, but that's an issue we aim to address soon.

All 10 comments

Instead of using Inject attribute, you need to inject HttpClient in the DataAccess constructor and add IDataAccess to the services collection:

publis class DataAccess : IDataAccess
{
  private readonly HttpClient _http;

  public DataAccess(HttpClient http)
  {
      _http = http;
  }
  ...
  void DoSomething()
  {
    // Here I would like to use _http
  }
}
class Program
{
    static void Main(string[] args)
    {
         var serviceProvider = new BrowserServiceProvider(services =>
         {
             // Add any custom services here
            services.AddSingleton<IDataAccess, DataAccess>();
         });

        new BrowserRenderer(serviceProvider).AddComponent<App>("app");
    }
}

Thank you for pointing that out, you are absolutely right. I was aware of this possibility. Still, I think that having the option to use the InjectAttribute in DataAccess would be useful. Developers coming from e.g. MEF would expect that because there it works like that. However, your solution definitely works and it is good that it is now documented in this issue. Thank you for that.

BTW - I have an example for constructor injection in my DI chapter on learn-blazor.com.

Oups :-/

I don't think that property injection is a feature of the standard Microsoft.Extensions.DependencyInjection.ServiceCollection that we use, both in Blazor and all other ASP.NET project types: https://stackoverflow.com/questions/38459625/property-injection-in-asp-net-core

As such I'm not sure it's a thing we have the option to do directly. However, we do already let developers plug in a third-party DI framework which could itself support property injection. In your Program.cs, instead of passing a BrowserServiceProvider to BrowserRenderer, instead construct some other IServiceProvider backed by your third-party DI framework, and pass that to BrowserRenderer. Your service resolution logic should probably do something like:

  • Test whether the service type is present in a BrowserServiceProvider, and if so, use that. This will preserve correct behavior for HttpClient etc.
  • If not, get the service instance from your third-party provider

... and actually register your IDataAccess implementation in the third-party provider so that you get its property injection behavior at runtime.

This is the same as for other ASP.NET project types, so I think the design is as it should be for Blazor. But please let us know if you think there's a better way we could do it (other than adding property injection to Microsoft.Extensions.DependencyInjection.ServiceCollection, which is not something we control in this repo).

Thank you @SteveSandersonMS for the detailed answer. Having the options of constructor injection and third-party DI framework is perfect. I think it is important to emphasize in the docs that InjectAttribute only works on components so that misunderstandings are reduced to a minimum.

Also to be clear, the primary reason why InjectAttribute exists is just to support the Razor @inject syntax.

We're not trying to stop developers from using [Inject] directly, but for Razor-based components, it should never be necessary. It's much nicer to use @inject TypeName PropertyName instead. People should rarely see [Inject] explicitly in their own code.

I'm guessing you're using it currently in a base class because of the lack of partial support, but that's an issue we aim to address soon.

I would also add that [Inject] does what it does in Razor (MVC and Blazor) because you don't usually write a constructor for the generated class.

I came across this issue while looking for a solution to my problem.
I have a Blazor class library with some Components and some Interop code.
Until now, using JSRuntime.Current was fine - but it has been removed from .NET Core 3.0 Preview 3.
In the Interop classes I need a reference to the injected IJsRuntime .
I stumble across on how to resolve this as the InjectAttribute doesn't work here.

@sven5 It's the same as with any other DI service - you have to get it from something that has access to the DI container (e.g., a component), and from there you pass it into any other infrastructure that needs it.

@SteveSandersonMS
Hi,

yes that's right - I found a workaround.
However, there should be another way to inject services not only to Components in a class library project - Or bring back the static JsRuntime access. :smirk:

Was this page helpful?
0 / 5 - 0 ratings