This project is an incremental roslyn source generator whose job it is to auto-generate the extension methods in
RegistrationExtensions
that take a delegate with a variable number of dynamically-typed arguments for injection.
For each number of arguments we accept, we must generate:
-
A
Register
extension method that takes a delegate containing each generic argument.public static IRegistrationBuilder<TComponent, SimpleActivatorData, SingleRegistrationStyle> Register<TDependency1, TDependency2, TComponent>( this ContainerBuilder builder, Func<TDependency1, TDependency2, TComponent> @delegate) where TDependency1 : notnull where TDependency2 : notnull { // ... snip ... }
-
A similar
Register
extension method that also takes anIComponentContext
argument as the first delegate argument before any generic types.public static IRegistrationBuilder<TComponent, SimpleActivatorData, SingleRegistrationStyle> Register<TDependency1, TDependency2, TComponent>( this ContainerBuilder builder, Func<IComponentContext, TDependency1, TDependency2, TComponent> @delegate) where TDependency1 : notnull where TDependency2 : notnull { // ... snip ... }
-
An open generic
DelegateInvoker
class derived fromAutofac.Core.Resolving.BaseGenericResolveDelegateInvoker
that holds the delegate for each registration.public sealed class DelegateInvoker2<TDependency1, TDependency2, TComponent> : BaseGenericResolveDelegateInvoker where TDependency1 : notnull where TDependency2 : notnull { private readonly Func<TDependency1, TDependency2, TComponent> _delegate; public DelegateInvoker2(Func<TDependency1, TDependency2, TComponent> @delegate) { _delegate = @delegate; } // ... snip ... }
-
An open generic
DelegateInvokerWithComponentContext
class, same as the preceding one, but supports theIComponentContext
argument to the delegate.public sealed class DelegateInvoker2WithComponentContext<TDependency1, TDependency2, TComponent> : BaseGenericResolveDelegateInvoker where TDependency1 : notnull where TDependency2 : notnull { private readonly Func<IComponentContext, TDependency1, TDependency2, TComponent> _delegate; public DelegateInvoker2WithComponentContext(Func<IComponentContext, TDependency1, TDependency2, TComponent> @delegate) { _delegate = @delegate; } // ... snip ... }
This project is only referenced by the Autofac project in this solution, and is not distributed as a NuGet package.
The files generated by this generator will end up in the obj/{config}/{tfm}/generated
folder in the Autofac project, because we have <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
turned on in the Autofac csproj file.
The tests for the code generator are in the Autofac.Test.CodeGen
project.
When making changes to the generator, the correct order of work to make sure your changes reliably show up in intellisense is:
- Update the generator code as you need.
- Run the tests in
Autofac.Test.CodeGen
; verify that the outputted code is what you expect. - When you're confident the output looks good, restart Visual Studio and VS Code to see your changes reflected.
Why the need to restart? Visual Studio and Omnisharp in VSCode load source generators for intellisense once, when they are first used, and does not unload them, so any subsequent changes are not picked up.
Running the build on the cli with
dotnet build
will correctly build the generator and use the latest generated code every time, so if you see some cases where your IDE isn't picking up the changes check the CLI build produces sensible results.