Repository navigation
Analyzer Extension Support
This page is for analyzer/source-generator authors.
This page describes analyzer extension support for external source generators (for example, generators that need to contribute additional DI registrations).
It is not the same as manual partial-class customization of DependencyRegistrator in consumer code.
For manual customization, see Manual Registrator Customization (Partial Class).
Source generators cannot directly see code generated by other source generators.
Because of that, Basilisque DI provides an extension mechanism so external analyzers can contribute additional registration code in a compatible way.
Install:
At a high level:
- your analyzer generates partial methods for Basilisque DI extension points
- Basilisque DI invokes extension partial methods when configured
- the setting
BAS_DI_Extensionscontrols which extension names are expected
The setting supports values separated by ; or ,.
The ExtensionSupport package provides utilities around:
- incremental generator value providers for root namespace/assembly name
- extension implementation output helpers
- service-registration code-line generation helpers
- Reference ExtensionSupport in your analyzer project.
- Register extension output using the provided incremental generator helper APIs.
- Generate extension method implementations for registration and/or dependency initialization.
- Configure consuming projects with matching
BAS_DI_Extensionsvalues.
Use this when your analyzer discovers implementation types and only needs to append service registrations.
using Basilisque.CodeAnalysis.Syntax;
using Basilisque.DependencyInjection.CodeAnalysis.ExtensionSupport.DependencyInjectionGenerator;
using Microsoft.CodeAnalysis;
using System.Collections.Immutable;
internal static class FeatureExtensionOutput
{
internal static void OutputImplementations(
SourceProductionContext context,
((ImmutableArray<INamedTypeSymbol> Types, INamedTypeSymbol ServiceInterface) Input, (string? RootNamespace, string? AssemblyName) DIInfo) data,
RegistrationOptions registrationOptions)
{
var extensionData = (data.Input, data.DIInfo);
DependencyInjectionExtensionGeneratorOutput.OutputImplementations(
context,
extensionData,
registrationOptions,
extensionName: "MY_FEATURE_DIExt",
registerExtensionCallback: AddRegistrations);
}
private static void AddRegistrations(
SourceProductionContext context,
CodeLines body,
(ImmutableArray<INamedTypeSymbol> Types, INamedTypeSymbol ServiceInterface) input)
{
var serviceType = input.ServiceInterface.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
foreach (var type in input.Types)
{
var impl = type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
body.Add($"services.AddTransient(typeof({serviceType}), typeof({impl}));");
}
}
}Use this when your extension needs both:
- dependency-chain initialization code
- service registration code
using Basilisque.CodeAnalysis.Syntax;
using Basilisque.DependencyInjection.CodeAnalysis.ExtensionSupport.DependencyInjectionGenerator;
using Microsoft.CodeAnalysis;
internal static class PluginExtensionOutput
{
internal static void OutputImplementations(
SourceProductionContext context,
((string PluginRegistratorFqn, string PluginServiceFqn) Input, (string? RootNamespace, string? AssemblyName) DIInfo) data,
RegistrationOptions registrationOptions)
{
DependencyInjectionExtensionGeneratorOutput.OutputImplementations(
context,
(data.Input, data.DIInfo),
registrationOptions,
extensionName: "MY_PLUGIN_DIExt",
initializeExtensionCallback: InitializeDependencies,
registerExtensionCallback: RegisterServices);
}
private static void InitializeDependencies(
SourceProductionContext context,
CodeLines body,
(string PluginRegistratorFqn, string PluginServiceFqn) input)
{
body.Add($"collection.AddDependency<{input.PluginRegistratorFqn}>();");
}
private static void RegisterServices(
SourceProductionContext context,
CodeLines body,
(string PluginRegistratorFqn, string PluginServiceFqn) input)
{
body.Add($"services.AddSingleton(typeof({input.PluginServiceFqn}));");
}
}The consuming project must configure matching extension names (separated by ; or ,).
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<BAS_DI_Extensions>MY_FEATURE_DIExt;MY_PLUGIN_DIExt</BAS_DI_Extensions>
</PropertyGroup>
</Project>Then normal registration is still a single call:
services.RegisterServices();If extension names do not match, the extension partial hooks are not wired as expected.
- Use stable identifiers (for example, vendor + feature prefix).
- Avoid spaces and special characters.
- Keep names deterministic so consumers can configure
BAS_DI_Extensionsreliably.
When configured correctly, consumers call services.RegisterServices() once, and both core Basilisque registrations and extension-contributed registrations are applied.