Package Description
$ dotnet add package burtonrodman.ServiceConstructorGeneratorA C# Source Generator that generates a constructor to initialize all readonly or required fields and/or required properties.
This reduces the amount of boiler-plate code needed when using constructor injection in ASP.Net Core projects for example. However, this can be used with any C# project and does NOT require ASP.Net Core, or even a Dependency Injection system.
Constructor Parameters are generated in source order.
burtonrodman.ServiceConstructorGenerator NuGet package to your project.burtonrodman.ServiceConstructorGenerator to the top of your C# file or as a global using.[GenerateServiceConstructor] attribute to your class.partial keyword on your class.readonly or required keywords.required keyword.
Suggestion: scope the property as
publicwith aprivate get;andinit;:<br/>public IWidgetRepository WidgetRepository { private get; init; }
[InjectAsOptions] attribute on any field/property that should be wrapped with IOptions.partial void OnAfterInitialized() in your class.In this example, the following constructor will be generated:
/// Usings.cs
global using burtonrodman.ServiceConstructorGenerator;
/// TestService.cs
namespace MyApp;
[GenerateServiceConstructor]
public partial class TestService
{
private readonly IHttpContextAccessor _accessor;
public required IWidgetRepository WidgetRepository { private get; init; };
[InjectAsOptions]
private readonly EmailSenderOptions _emailSenderOptions;
partial void OnAfterInitialized()
{
// add your logic here
}
}
Generated Code:
/// TestService.g.cs
namespace MyApp
{
public partial class Test
{
partial void OnAfterInitialized();
public Test(
IHttpContextAccessor _accessor,
IWidgetRepository WidgetRepository,
Microsoft.Extensions.Options.IOptions<EmailSenderOptions> _emailSenderOptions
) {
this._accessor = _accessor ?? throw new ArgumentNullException(nameof(_accessor));
this.WidgetRepository = WidgetRepository ?? throw new ArgumentNullException(nameof(WidgetRepository));
this._emailSenderOptions = _emailSenderOptions.Value ?? throw new ArgumentNullException(nameof(_emailSenderOptions));
OnAfterInitialized();
}
}
}
Base class parameters may be passed along by providing them in the GenerateServiceConstructor attribute:
[GenerateServiceConstructor("register", "Register stuff")]
public partial class Test : Command
{
...
}
Generated code:
public partial class Test
{
public Test(
...
) : base("register", "Register stuff") {
...
}
}
This is currently implemented as a verbatim copy of the attribute's parameter list -- no parsing or type-checking occurs.
The type or namespace name 'SetsRequiredMembersAttribute' does not exist in namespace 'System.Diagnostics.CodeAnalysis'
namespace System.Diagnostics.CodeAnalysis;
// stub in for now since we're not on C# 11
[AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false)]
public sealed class SetsRequiredMembersAttribute : Attribute
{
}
I welcome Pull Requests for any improvement or bug fixes. Please open an Issue for discussion if you plan on adding any features, so that we can collaborate on design. For bug reports, a Pull Request with a failing unit test is ideal.
Thanks!