-
-
Notifications
You must be signed in to change notification settings - Fork 64
Blazor (.NET9+): migrate property injection to constructor injection #1047
Description
Is your feature request related to a problem? Please describe.
Since .NET9, it is possible to have constructor injection in Blazor components, instead of property injection: https://learn.microsoft.com/en-us/aspnet/core/release-notes/aspnetcore-9.0?view=aspnetcore-10.0#constructor-injection / https://learn.microsoft.com/en-us/aspnet/core/blazor/fundamentals/dependency-injection?view=aspnetcore-9.0#request-a-service-in-a-component
It would be nice to have an analyzer and a fix that helps detect and migrate property injection to constructor injection in Blazor components.
Describe the solution you'd like
Considering this code:
using Microsoft.AspNetCore.Components;
public partial class Component
{
[Inject]
protected NavigationManager Navigation { get; set; } = default!;
private void HandleClick()
{
NavigationManager.NavigateTo("/counter");
}
}the injected dependency should be marked as a suggestion of fix, with the fix that would change the property to a constructor parameter like this:
using Microsoft.AspNetCore.Components;
public partial class Component(NavigationManager navigation)
{
private void HandleClick()
{
navigation.NavigateTo("/counter");
}
}Describe alternatives you've considered
N/A
Additional context
I'm not sure if anyone would prefer the other way around? Detecting constructor injection to migrate to property injection? Constructor injection seems like an improvement (avoid defaut! and in somes cases, avoid more complicated init in OnInitializedAsync ).
The presence on an existing constructor in the class could complicate the fix, especially if async is involved :
partial class Home
{
public Home(AdminClientService downstreamApi)
{
_downstreamApi = downstreamApi;
}
int _numberOfDevices;
string _version = "v.?";
readonly AdminClientService _downstreamApi;
[Inject] IWebHostEnvironment _env { get; set; } = default!;
protected override async Task OnInitializedAsync()
{
ICollection<DeviceListDto> devices = await _downstreamApi.GetDeviceListAsync()
.ConfigureAwait(true);
_numberOfDevices = devices.Count;
_version = await CloudVersionService.GetCloudVersionAsync(_env.WebRootPath)
.ConfigureAwait(false) ?? "";
}
}would need to migrate to:
partial class Home
{
public Home(AdminClientService downstreamApi,
IWebHostEnvironment env)
{
_downstreamApi = downstreamApi;
_env = env;
}
int _numberOfDevices;
string _version = "v.?";
readonly AdminClientService _downstreamApi;
readonly IWebHostEnvironment _env;
protected override async Task OnInitializedAsync()
{
ICollection<DeviceListDto> devices = await _downstreamApi.GetDeviceListAsync()
.ConfigureAwait(true);
_numberOfDevices = devices.Count;
_version = await CloudVersionService.GetCloudVersionAsync(_env.WebRootPath)
.ConfigureAwait(false) ?? "";
}
}Then IDE0290 could complete the job to migrate to primary constructor to get close to the simpler example from the doc.