Skip to content

Commit 890d624

Browse files
committed
Added support for other dependency injection containers (e.g. Autofac)
- Added support for other dependency injection containers (e.g. Autofac) when only `Microsoft.Extensions.DependencyInjection.Abstractions` package (version >= 2.1.1) is added to the project. You can set your custom service provider with the extension method `Cli.Ext.SetServiceProvider`. In previous version, this method already existed but accepted a `ServiceProvider` parameter, instead of `IServiceProvider` parameter which allows 3rd party implementations other than the default one in `Microsoft.Extensions.DependencyInjection`. - Reduced minimum version requirement for `Microsoft.Extensions.DependencyInjection` from `6.0.0` to `2.1.1` so that you don't need to update it in legacy projects. Added `Cli.Ext.GetServiceCollection` and `Cli.Ext.GetServiceProviderOrDefault` extension methods.
1 parent 4e220e3 commit 890d624

File tree

206 files changed

+689
-492
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

206 files changed

+689
-492
lines changed

docs/README.md

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -766,7 +766,7 @@ public class ValidationCliCommand
766766

767767
## Dependency Injection
768768

769-
Commands can have injected dependencies, this is supported via `Microsoft.Extensions.DependencyInjection` package (version >= 6.0.0).
769+
Commands can have injected dependencies, this is supported via `Microsoft.Extensions.DependencyInjection` package (version >= 2.1.1).
770770
In your project directory, via dotnet cli:
771771
```console
772772
dotnet add package Microsoft.Extensions.DependencyInjection
@@ -852,6 +852,32 @@ public sealed class SingletonClass : IDisposable
852852
public void Dispose() => Console.WriteLine($"{nameof(SingletonClass)}.Dispose()");
853853
}
854854
```
855+
Other dependency injection containers (e.g. Autofac) are also supported
856+
via `Microsoft.Extensions.DependencyInjection.Abstractions` package (version >= 2.1.1).
857+
In your project directory, via dotnet cli:
858+
```console
859+
dotnet add package Microsoft.Extensions.DependencyInjection.Abstractions
860+
```
861+
or in Visual Studio Package Manager Console:
862+
```console
863+
PM> Install-Package Microsoft.Extensions.DependencyInjection.Abstractions
864+
```
865+
When the source generator detects that your project has reference to `Microsoft.Extensions.DependencyInjection.Abstractions`,
866+
it will generate extension methods for supporting custom service providers.
867+
For example, you can now set your custom service provider with the extension method `Cli.Ext.SetServiceProvider`:
868+
```c#
869+
using DotMake.CommandLine;
870+
using Autofac.Core;
871+
using Autofac.Core.Registration;
872+
873+
var cb = new ContainerBuilder();
874+
cb.RegisterType<object>();
875+
var container = cb.Build();
876+
877+
Cli.Ext.SetServiceProvider(container);
878+
879+
Cli.Run<RootCliCommand>();
880+
```
855881
## Help output
856882

857883
When you run the app via

src/Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<!-- https://learn.microsoft.com/en-us/dotnet/core/project-sdk/msbuild-props#generateassemblyinfo -->
5-
<VersionPrefix>1.8.0</VersionPrefix>
5+
<VersionPrefix>1.8.1</VersionPrefix>
66
<Product>DotMake Command-Line</Product>
77
<Company>DotMake</Company>
88
<!-- Copyright is also used for NuGet metadata -->
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using Microsoft.Extensions.DependencyInjection;
2+
using System;
3+
4+
namespace DotMake.CommandLine
5+
{
6+
/// <summary>
7+
/// Provides <see cref="IServiceCollection"/> related extension methods for <see cref="Cli"/> services feature.
8+
/// <para>
9+
/// Requires dependency <c>Microsoft.Extensions.DependencyInjection (>= 2.1.1)</c>.
10+
/// <br/>Default implementation <see cref="ServiceCollection"/> is in <c>Microsoft.Extensions.DependencyInjection</c> assembly.
11+
/// </para>
12+
/// </summary>
13+
public static class CliServiceCollectionExtensions
14+
{
15+
private static readonly IServiceCollection ServiceCollection = new ServiceCollection();
16+
private static IServiceProvider serviceProvider;
17+
18+
/// <summary>
19+
/// Registers services into the <see cref="Cli"/>'s default service collection.
20+
/// </summary>
21+
/// <param name="ext">The CliExtensions instance to extend.</param>
22+
/// <param name="configure">An <see cref="Action{IServiceCollection}"/> to configure the <see cref="Cli"/>'s default service collection.</param>
23+
public static void ConfigureServices(this CliExtensions ext, Action<IServiceCollection> configure)
24+
{
25+
configure(ServiceCollection);
26+
}
27+
28+
/// <summary>
29+
/// Gets the <see cref="Cli"/>'s default service collection.
30+
/// </summary>
31+
/// <param name="ext">The CliExtensions instance to extend.</param>
32+
/// <returns>A <see cref="IServiceCollection"/> instance.</returns>
33+
public static IServiceCollection GetServiceCollection(this CliExtensions ext)
34+
{
35+
return ServiceCollection;
36+
}
37+
38+
/// <summary>
39+
/// Gets the service provider built from <see cref="Cli"/>'s default service collection (built on first access).
40+
/// If <see cref="CliServiceProviderExtensions.SetServiceProvider"/> was used, then gets the custom <see cref="IServiceProvider"/> that was set.
41+
/// </summary>
42+
/// <param name="ext">The CliExtensions instance to extend.</param>
43+
/// <returns>A <see cref="IServiceProvider"/> instance.</returns>
44+
public static IServiceProvider GetServiceProviderOrDefault(this CliExtensions ext)
45+
{
46+
return serviceProvider ??= CliServiceProviderExtensions.GetServiceProvider(null)
47+
?? ServiceCollection.BuildServiceProvider();
48+
}
49+
}
50+
}

src/DotMake.CommandLine.SourceGeneration.Embedded/CliServiceExtensions.cs

Lines changed: 0 additions & 46 deletions
This file was deleted.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System;
2+
using Microsoft.Extensions.DependencyInjection;
3+
4+
namespace DotMake.CommandLine
5+
{
6+
/// <summary>
7+
/// Provides <see cref="IServiceProvider"/> related extension methods for <see cref="Cli"/> services feature.
8+
/// <para>
9+
/// Requires dependency <c>Microsoft.Extensions.DependencyInjection.Abstractions (>= 2.1.1)</c>.
10+
/// <br/>Although <see cref="IServiceProvider"/> is in <c>System.ComponentModel</c> assembly,
11+
/// used class <see cref="ActivatorUtilities"/> is in <c>Microsoft.Extensions.DependencyInjection.Abstractions</c> assembly.
12+
/// </para>
13+
/// </summary>
14+
public static class CliServiceProviderExtensions
15+
{
16+
private static IServiceProvider serviceProvider;
17+
18+
/// <summary>
19+
/// Gets the custom service provider.
20+
/// If <see cref="SetServiceProvider"/> was used, then gets the custom <see cref="IServiceProvider"/> that was set.
21+
/// </summary>
22+
/// <param name="ext">The CliExtensions instance to extend.</param>
23+
/// <returns>A <see cref="IServiceProvider"/> instance or <see langword="null"/>.</returns>
24+
public static IServiceProvider GetServiceProvider(this CliExtensions ext)
25+
{
26+
return serviceProvider;
27+
}
28+
29+
/// <summary>
30+
/// Sets a custom service provider built from a custom service collection (to override the internal default one).
31+
/// When <see cref="GetServiceProvider"/> is called, this custom <see cref="IServiceProvider"/> will be returned.
32+
/// </summary>
33+
/// <param name="ext">The CliExtensions instance to extend.</param>
34+
/// <param name="customServiceProvider">The custom <see cref="IServiceProvider"/> instance to use.</param>
35+
public static void SetServiceProvider(this CliExtensions ext, IServiceProvider customServiceProvider)
36+
{
37+
serviceProvider = customServiceProvider;
38+
}
39+
}
40+
}

src/DotMake.CommandLine.SourceGeneration.Embedded/DotMake.CommandLine.SourceGeneration.Embedded.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
</PropertyGroup>
88

99
<ItemGroup>
10-
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" PrivateAssets="all" />
10+
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.1.1" PrivateAssets="all" />
1111
</ItemGroup>
1212

1313
<ItemGroup>

src/DotMake.CommandLine.SourceGeneration.Embedded/README.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,12 @@ Code should compile against DotMake.CommandLine.dll (not project due to circular
77
which will be available during runtime.
88

99
Make sure it compiles for lowest supported langversion 9.0 as source may be generated in a netstandard2.0 project.
10-
- `ModuleInitializerAttribute` requires LangVersion 9.0 (polyfill injected only when attribute does not exist).
10+
- `ModuleInitializerAttribute` requires LangVersion 9.0 (polyfill injected only when attribute does not exist).
1111
If your target framework is below net5.0, you need `<LangVersion>9.0</LangVersion>` tag (minimum) in your .csproj file.
12-
- `RequiredMemberAttribute` requires LangVersion 11.0 (polyfill injected only when 11+ and attribute does not exist).
12+
- `RequiredMemberAttribute` requires LangVersion 11.0 (polyfill injected only when 11+ and attribute does not exist).
1313
If your target framework is below net7.0, you need `<LangVersion>11.0</LangVersion>` tag (minimum) in your .csproj file
14-
- `CliServiceExtensions` feature injected only when project references `Microsoft.Extensions.DependencyInjection`.
14+
- `CliServiceProviderExtensions` feature injected only when project references `Microsoft.Extensions.DependencyInjection.Abstractions (>= 2.1.1)`.
15+
Although `IServiceProvider` is in `System.ComponentModel` assembly,
16+
used class `ActivatorUtilities` is in `Microsoft.Extensions.DependencyInjection.Abstractions` assembly.
17+
- `CliServiceCollectionExtensions` feature injected only when project references `Microsoft.Extensions.DependencyInjection (>= 2.1.1)`.
18+
Default implementation `ServiceCollection` is in `Microsoft.Extensions.DependencyInjection` assembly.

src/DotMake.CommandLine.SourceGeneration/CliCommandGenerator.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ public void Initialize(IncrementalGeneratorInitializationContext initializationC
2929

3030
var cliCommandInfos = initializationContext.SyntaxProvider.ForAttributeWithMetadataName(
3131
CliCommandInfo.AttributeFullName,
32-
3332
(syntaxNode, _) => CliCommandInfo.IsMatch(syntaxNode),
3433
(attributeSyntaxContext, _) => CliCommandInfo.From(attributeSyntaxContext)
3534
);
@@ -88,10 +87,16 @@ private static void GenerateReferenceDependantSourceCode(SourceProductionContext
8887
GetSourceTextFromEmbeddedResource("RequiredMemberAttribute.cs", analyzerConfigOptions)
8988
);
9089

90+
if (referenceDependantInfo.HasMsDependencyInjectionAbstractions)
91+
sourceProductionContext.AddSource(
92+
"(CliServiceProviderExtensions).g.cs",
93+
GetSourceTextFromEmbeddedResource("CliServiceProviderExtensions.cs", analyzerConfigOptions)
94+
);
95+
9196
if (referenceDependantInfo.HasMsDependencyInjection)
9297
sourceProductionContext.AddSource(
93-
"(CliServiceExtensions).g.cs",
94-
GetSourceTextFromEmbeddedResource("CliServiceExtensions.cs", analyzerConfigOptions)
98+
"(CliServiceCollectionExtensions).g.cs",
99+
GetSourceTextFromEmbeddedResource("CliServiceCollectionExtensions.cs", analyzerConfigOptions)
95100
);
96101

97102
}

src/DotMake.CommandLine.SourceGeneration/CliCommandInfo.cs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ private void Analyze()
175175
if (Symbol.IsAbstract || Symbol.IsGenericType)
176176
AddDiagnostic(DiagnosticDescriptors.ErrorClassNotNonAbstractNonGeneric, DiagnosticName);
177177

178-
if (!ReferenceDependantInfo.HasMsDependencyInjection
178+
if (!ReferenceDependantInfo.HasMsDependencyInjectionAbstractions
179179
&& !Symbol.InstanceConstructors.Any(c =>
180180
c.Parameters.IsEmpty
181181
&& (c.DeclaredAccessibility == Accessibility.Public || c.DeclaredAccessibility == Accessibility.Internal)
@@ -246,17 +246,27 @@ public void AppendCSharpDefineString(CodeStringBuilder sb, bool addNamespaceBloc
246246

247247
using (sb.AppendBlockStart($"private {definitionClass} CreateInstance()"))
248248
{
249-
if (ReferenceDependantInfo.HasMsDependencyInjection)
249+
if (ReferenceDependantInfo.HasMsDependencyInjectionAbstractions || ReferenceDependantInfo.HasMsDependencyInjection)
250250
{
251-
sb.AppendLine("var serviceProvider = DotMake.CommandLine.CliServiceExtensions.GetServiceProvider(null);");
251+
sb.AppendLine(ReferenceDependantInfo.HasMsDependencyInjection
252+
? "var serviceProvider = DotMake.CommandLine.CliServiceCollectionExtensions.GetServiceProviderOrDefault(null);"
253+
: "var serviceProvider = DotMake.CommandLine.CliServiceProviderExtensions.GetServiceProvider(null);");
254+
sb.AppendLine("if (serviceProvider != null)");
255+
sb.AppendIndent();
252256
sb.AppendLine("return Microsoft.Extensions.DependencyInjection.ActivatorUtilities");
253257
sb.AppendIndent();
258+
sb.AppendIndent();
254259
sb.AppendLine($".CreateInstance<{definitionClass}>(serviceProvider);");
255-
}
256-
else if (memberHasRequiredModifier)
260+
//in case serviceProvider is null (i.e. not set with SetServiceProvider)
261+
//call Activator.CreateInstance which will throw exception if class has no default constructor
262+
//but at least it avoids compile time error in generated code with new()
263+
sb.AppendLine();
257264
sb.AppendLine($"return System.Activator.CreateInstance<{definitionClass}>();");
265+
}
258266
else
259-
sb.AppendLine($"return new {definitionClass}();");
267+
sb.AppendLine(memberHasRequiredModifier
268+
? $"return System.Activator.CreateInstance<{definitionClass}>();"
269+
: $"return new {definitionClass}();");
260270
}
261271
sb.AppendLine();
262272

src/DotMake.CommandLine.SourceGeneration/DotMake.CommandLine.SourceGeneration.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,8 @@
5353
<EmbeddedResource Remove="..\DotMake.CommandLine.SourceGeneration.Embedded\obj\**\*" />
5454
</ItemGroup>
5555

56+
<ItemGroup>
57+
<EmbeddedResource Remove="..\DotMake.CommandLine.SourceGeneration.Embedded\CliServiceExtensions.cs" />
58+
</ItemGroup>
59+
5660
</Project>

0 commit comments

Comments
 (0)