Skip to content

Commit 18c1211

Browse files
Support for Roots
1 parent 3ef4d8c commit 18c1211

File tree

5 files changed

+134
-6
lines changed

5 files changed

+134
-6
lines changed

samples/WebAPI/Composition.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ namespace WebAPI;
55

66
using Pure.DI;
77
using Controllers;
8+
using Microsoft.AspNetCore.Mvc;
89
using Pure.DI.MS;
910
using WeatherForecast;
1011
using static Pure.DI.Lifetime;
@@ -15,6 +16,6 @@ static void Setup() => DI.Setup()
1516
// Use the DI setup from the base class
1617
.DependsOn(Base)
1718
.Bind().As(Singleton).To<WeatherForecastService>()
18-
// Provides the composition root for Weather Forecast controller
19-
.Root<WeatherForecastController>();
19+
// Provides controllers as roots
20+
.Roots<ControllerBase>();
2021
}

samples/WebApp/Composition.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ namespace WebApp;
66
using Pure.DI;
77
using Pure.DI.MS;
88
using Controllers;
9+
using Microsoft.AspNetCore.Mvc;
910
using WeatherForecast;
1011
using static Pure.DI.Lifetime;
1112

@@ -15,6 +16,6 @@ static void Setup() => DI.Setup()
1516
// Use the DI setup from the base class
1617
.DependsOn(Base)
1718
.Bind().As(Singleton).To<WeatherForecastService>()
18-
// Provides the composition root for Home controller
19-
.Root<HomeController>();
19+
// Provides controllers as roots
20+
.Roots<Controller>();
2021
}

src/Pure.DI.Core/Components/Api.g.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2048,10 +2048,25 @@ internal interface IConfiguration
20482048
/// </summary>
20492049
/// <param name="name">Specifies the unique name of the root of the composition. If the value is empty, a private root will be created, which can be used when calling <c>Resolve</c> methods.</param>
20502050
/// <param name="tag">Optional argument specifying the tag for the root of the Composition.</param>
2051+
/// <param name="kind">The Optional argument specifying the kind for the root of the Composition.</param>
20512052
/// <typeparam name="T">The Composition root type.</typeparam>
20522053
/// <returns>Reference to the setup continuation chain.</returns>
20532054
IConfiguration Root<T>(string name = "", object tag = null, RootKinds kind = RootKinds.Default);
20542055

2056+
/// <summary>
2057+
/// Specifying the roots of the Composition.
2058+
/// <example>
2059+
/// <code>
2060+
/// DI.Setup("Composition")
2061+
/// .Roots&lt;IService&gt;();
2062+
/// </code>
2063+
/// </example>
2064+
/// </summary>
2065+
/// <param name="kind">The Optional argument specifying the kind for the root of the Composition.</param>
2066+
/// <typeparam name="T">The Composition root base type.</typeparam>
2067+
/// <returns>Reference to the setup continuation chain.</returns>
2068+
IConfiguration Roots<T>(RootKinds kind = RootKinds.Default);
2069+
20552070
/// <summary>
20562071
/// Specifies the method of the composition builder. The first argument to the method will always be the instance to be built. The remaining arguments to this method will be listed in the order in which they are defined in the setup.Specifies to create a composition builder method. The first argument to the method will always be the instance to be built. The remaining arguments to this method will be listed in the order in which they are defined in the setup.
20572072
/// <example>
@@ -2062,6 +2077,7 @@ internal interface IConfiguration
20622077
/// </example>
20632078
/// </summary>
20642079
/// <param name="name">Specifies the unique name of the builder. The default name is "BuildUp".</param>
2080+
/// <param name="kind">The Optional argument specifying the kind for the root of the Composition.</param>
20652081
/// <typeparam name="T">The Composition root type.</typeparam>
20662082
/// <returns>Reference to the setup continuation chain.</returns>
20672083
IConfiguration Builder<T>(string name = "BuildUp", RootKinds kind = RootKinds.Default);
@@ -2076,6 +2092,7 @@ internal interface IConfiguration
20762092
/// </example>
20772093
/// </summary>
20782094
/// <param name="name">Specifies the unique name of the builders. The default name is "BuildUp".</param>
2095+
/// <param name="kind">The Optional argument specifying the kind for the root of the Composition.</param>
20792096
/// <typeparam name="T">The Composition root base type.</typeparam>
20802097
/// <returns>Reference to the setup continuation chain.</returns>
20812098
IConfiguration Builders<T>(string name = "BuildUp", RootKinds kind = RootKinds.Default);
@@ -2945,12 +2962,19 @@ public IConfiguration RootArg<T>(string name, params object[] tags)
29452962
return Configuration.Shared;
29462963
}
29472964

2965+
29482966
/// <inheritdoc />
29492967
public IConfiguration Root<T>(string name, object tag, RootKinds rootKind)
29502968
{
29512969
return Configuration.Shared;
29522970
}
29532971

2972+
/// <inheritdoc />
2973+
public IConfiguration Roots<T>(RootKinds kind = RootKinds.Default)
2974+
{
2975+
return Configuration.Shared;
2976+
}
2977+
29542978
/// <inheritdoc />
29552979
public IConfiguration Builder<T>(string name = "BuildUp", RootKinds kind = RootKinds.Default)
29562980
{

src/Pure.DI.Core/Core/ApiInvocationProcessor.cs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -278,13 +278,36 @@ MemberAccessExpressionSyntax memberAccess when memberAccess.Kind() == SyntaxKind
278278
VisitArg(metadataVisitor, semanticModel, ArgKind.Root, invocation, genericName, invocationComments);
279279
break;
280280

281+
case nameof(IConfiguration.Roots):
282+
if (genericName.TypeArgumentList.Arguments is not [{ } rootsTypeSyntax]
283+
|| semantic.TryGetTypeSymbol<ITypeSymbol>(semanticModel, rootsTypeSyntax) is not {} rootsType)
284+
{
285+
return;
286+
}
287+
288+
var rootsArgs = arguments.GetArgs(invocation.ArgumentList, "kind");
289+
var kind = rootsArgs[0] is { } kindArg ? semantic.GetConstantValue<RootKinds>(semanticModel, kindArg.Expression) : RootKinds.Default;
290+
var rootTypes = semanticModel
291+
.LookupNamespacesAndTypes(invocation.Span.Start)
292+
.OfType<INamedTypeSymbol>()
293+
.Where(i => !i.IsAbstract && !i.IsUnboundGenericType)
294+
.Where(type => baseSymbolsProvider.GetBaseSymbols(type, (_, _) => true).Any(typeSymbol => SymbolEqualityComparer.Default.Equals(rootsType, typeSymbol)))
295+
.ToList();
296+
297+
foreach (var rootType in rootTypes)
298+
{
299+
metadataVisitor.VisitRoot(new MdRoot(invocation, semanticModel, rootType, "", new MdTag(0, null), kind, invocationComments, false));
300+
}
301+
302+
break;
303+
281304
case nameof(IConfiguration.Root):
282-
if (genericName.TypeArgumentList.Arguments is not [{ } rootType])
305+
if (genericName.TypeArgumentList.Arguments is not [{ } rootTypeSyntax])
283306
{
284307
return;
285308
}
286309

287-
var rootSymbol = semantic.GetTypeSymbol<INamedTypeSymbol>(semanticModel, rootType);
310+
var rootSymbol = semantic.GetTypeSymbol<INamedTypeSymbol>(semanticModel, rootTypeSyntax);
288311
VisitRoot(metadataVisitor, semanticModel, invocation, invocationComments, rootSymbol);
289312
break;
290313

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
namespace Pure.DI.IntegrationTests;
2+
3+
public class RootsTests
4+
{
5+
[Fact]
6+
public async Task ShouldSupportRoots()
7+
{
8+
// Given
9+
10+
// When
11+
var result = await """
12+
using System;
13+
using Pure.DI;
14+
15+
namespace Sample
16+
{
17+
interface IDependency {}
18+
19+
class Dependency: IDependency
20+
{
21+
}
22+
23+
interface IService
24+
{
25+
IDependency? Dep { get; }
26+
}
27+
28+
class Service: IService
29+
{
30+
[Ordinal(1)]
31+
internal void Initialize([Tag(374)] string depName)
32+
{
33+
Console.WriteLine($"Initialize 1 {depName}");
34+
}
35+
36+
[Ordinal(0)]
37+
public IDependency? Dep { get; set; }
38+
}
39+
40+
class Service2: IService
41+
{
42+
[Ordinal(1)]
43+
internal void Initialize([Tag(374)] string depName)
44+
{
45+
Console.WriteLine($"Initialize 2 {depName}");
46+
}
47+
48+
[Ordinal(0)]
49+
public IDependency? Dep { get; set; }
50+
}
51+
52+
static class Setup
53+
{
54+
private static void SetupComposition()
55+
{
56+
DI.Setup("Composition")
57+
.Bind(374).To(_ => "Abc")
58+
.Bind().To<Dependency>()
59+
.Roots<IService>();
60+
}
61+
}
62+
63+
public class Program
64+
{
65+
public static void Main()
66+
{
67+
var composition = new Composition();
68+
var service = composition.Resolve<Service>();
69+
var service2 = composition.Resolve<Service2>();
70+
}
71+
}
72+
}
73+
""".RunAsync();
74+
75+
// Then
76+
result.Success.ShouldBeTrue(result);
77+
result.StdOut.ShouldBe(["Initialize 1 Abc", "Initialize 2 Abc"], result);
78+
}
79+
}

0 commit comments

Comments
 (0)