Awesome
Excubo.Generators.Blazor
This project improves the performance of Blazor components using source generators and provides helpful diagnostics.
Installation
Nuget
Excubo.Generators.Blazor is distributed via nuget.org.
Package Manager:
Install-Package Excubo.Generators.Blazor
.NET Cli:
dotnet add package Excubo.Generators.Blazor
Package Reference
<PackageReference Include="Excubo.Generators.Blazor" />
Project settings
Since roslyn does not support roslyn source generator dependencies, the razor source generator is incompatible with this project. You need to opt-out using the setting
<UseRazorSourceGenerator>false</UseRazorSourceGenerator>
in your project file.
Example:
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<UseRazorSourceGenerator>false</UseRazorSourceGenerator>
</PropertyGroup>
...
</Project>
SetParametersAsync Source Generator
How does it work
Blazor uses C#-Reflection to handle the setting of component's [Parameter]
s which is slower than a compile-time approach.
The SetParametersAsync
generator overrides the default reflection-based implementation of Task SetParametersAsync(ParameterView parameters)
following this
recommendation by MS.
This increases the performance of setting parameters of components up to 6x.
How to enable
Add @attribute [Excubo.Generators.Blazor.GenerateSetParametersAsync]
to your _Imports.razor
file. This enables the source generator on all components.
As sometimes you might want to override the method yourself, you can opt-out of the source generator by adding @attribute [Excubo.Generators.Blazor.DoNotGenerateSetParametersAsync]
to a component.
You can use [GenerateSetParametersAsync(RequireExactMatch = true)]
, if you do not require parameters to match when they differ in case.
Implementation details
If you write the code
using Excubo.Generators.Blazor;
using Microsoft.AspNetCore.Components;
namespace IntegrationTest
{
[GenerateSetParametersAsync]
public partial class Component : ComponentBase
{
[Parameter] public string Parameter1 { get; set; }
// usually you have more parameters
}
}
the source generator generates
using Microsoft.AspNetCore.Components;
using System.Threading.Tasks;
using System;
namespace IntegrationTest
{
public partial class Component
{
public override Task SetParametersAsync(ParameterView parameters)
{
foreach (var parameter in parameters)
{
BlazorImplementation__WriteSingleParameter(parameter.Name, parameter.Value);
}
// Run the normal lifecycle methods, but without assigning parameters again
return base.SetParametersAsync(ParameterView.Empty);
}
private void BlazorImplementation__WriteSingleParameter(string name, object value)
{
switch (name) // parameter properties are actually case insensitive. This is ignored here for performance, but handled later for correctness
{
case "Parameter1":
this.Parameter1 = (string)value;
break;
// more parameters would create more cases
default:
{
switch (name.ToLowerInvariant()) // parameter properties are actually case insensitive.
{
case "parameter1":
this.Parameter1 = (string)value;
break;
// more parameters would create more cases
default:
throw new ArgumentException($"Unknown parameter: {name}");
}
break;
}
}
}
}
}
Diagnostic for missing @key
in loops
There is a common source of issues when loops are used in Blazor without assigning @key
s to the contained elements/components. This leads to performance issues, and can also lead to issues with correctness (e.g. when it is important which component gets disposed).
By installing this nuget package, you get warnings when you forget to set @key
:
@foreach (var element in items)
~~~~~~~
Warning: A key must be used when rendering loops in Blazor
{
<div class="my-component">
@element
</div>
}
Experimental diagnostic: required parameters
In some situations it's an advantage to be able to mark parameters as required. With this package you have two ways to say that:
- explicitly required
@code {
[Required][Parameter] public T Value { get; set; }
}
- all parameters are required
// either in your Component.razor or in _Imports.razor
@attribute [Excubo.Generators.Blazor.ParametersAreRequiredByDefault]