Skip to content

Commit 4b4e051

Browse files
#88 Support for contextual/scoped dependency overrides
1 parent 8244f48 commit 4b4e051

22 files changed

Lines changed: 510 additions & 83 deletions

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2994,8 +2994,6 @@ internal interface IContext
29942994
/// var service = new Service();
29952995
/// // Initialize an instance with all necessary dependencies
29962996
/// ctx.BuildUp(service);
2997-
///
2998-
///
29992997
/// return service;
30002998
/// })
30012999
/// </code>
@@ -3006,7 +3004,7 @@ internal interface IContext
30063004
/// <seealso cref="IBinding.To{T}(System.Func{Pure.DI.IContext,T})"/>
30073005
void BuildUp<T>(T value);
30083006

3009-
void Override<T>(T value, object tag = null);
3007+
void Override<T>(T value, params object[] tags);
30103008
}
30113009

30123010
/// <summary>

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

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ sealed class ApiInvocationProcessor(
2323
: IApiInvocationProcessor
2424
{
2525
private static readonly char[] TypeNamePartsSeparators = ['.'];
26+
private static object NullTag = new object();
2627

2728
public void ProcessInvocation(
2829
IMetadataVisitor metadataVisitor,
@@ -817,12 +818,12 @@ private MdOverride CreateOverride(
817818
ref bool hasContextTag)
818819
{
819820
var invocation = @override.Expression;
820-
if (invocation.ArgumentList.Arguments is not { Count: 1 or 2 })
821+
if (invocation.ArgumentList.Arguments.Count == 0)
821822
{
822823
return default;
823824
}
824825

825-
var args = arguments.GetArgs(invocation.ArgumentList, "value", "tag");
826+
var args = arguments.GetArgs(invocation.ArgumentList, "value", "tags");
826827
if (args[0] is not {} valueArg)
827828
{
828829
return default;
@@ -834,19 +835,22 @@ private MdOverride CreateOverride(
834835
return default;
835836
}
836837

837-
var tag = args[1]?.Expression;
838-
var hasCtx = HasContextTag(tag, contextParameter);
839-
hasContextTag |= hasCtx;
840-
var tagValue = hasCtx ? MdTag.ContextTag : tag is null ? null : semantic.GetConstantValue<object>(semanticModel, tag);
841-
var resolverTag = new MdTag(0, tagValue);
838+
var tagArguments = invocation.ArgumentList.Arguments.Skip(1).ToList();
839+
var hasCtx = tagArguments.Aggregate(false, (current, tag) => current | HasContextTag(tag.Expression, contextParameter));
840+
var tags = BuildTags(semanticModel, tagArguments)
841+
.AsEnumerable()
842+
.Select(tag => tag with { Value = tag.Value ?? (hasCtx ? MdTag.ContextTag : null) })
843+
.DefaultIfEmpty(new MdTag(0, hasCtx ? MdTag.ContextTag : null))
844+
.ToImmutableArray();
845+
842846
var valueExpression = (ExpressionSyntax)localVariableRenamingRewriter.Rewrite(semanticModel, false, true, valueArg.Expression);
843847
return new MdOverride(
844848
semanticModel,
845849
invocation,
846-
overrideIdProvider.GetId(argType, tagValue),
850+
overrideIdProvider.GetId(argType, tags.Select(tag => tag.Value ?? NullTag).ToImmutableHashSet()),
847851
@override.Position,
848852
argType,
849-
resolverTag,
853+
tags,
850854
valueExpression);
851855
}
852856

src/Pure.DI.Core/Core/ArgDependencyNodeBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public IEnumerable<DependencyNode> Build(DependencyNodeBuildContext ctx)
1313
continue;
1414
}
1515

16-
yield return new DependencyNode(0, binding, Arg: new DpArg(arg, binding));
16+
yield return new DependencyNode(0, binding, ctx.TypeConstructor, Arg: new DpArg(arg, binding));
1717
}
1818
}
1919
}

src/Pure.DI.Core/Core/Code/CompositionBuilder.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ public CompositionCode Build(DependencyGraph graph)
4141
foreach (var perResolveVar in map.GetPerResolves())
4242
{
4343
ctx.Code.AppendLine($"var {perResolveVar.VariableName} = default({typeResolver.Resolve(graph.Source, perResolveVar.InstanceType)});");
44-
if (perResolveVar.Info.RefCount > 0 && perResolveVar.InstanceType.IsValueType)
44+
if (perResolveVar.Info.RefCount > 0
45+
&& perResolveVar.InstanceType.IsValueType
46+
&& perResolveVar.Node.Construct is not { Source.Kind: MdConstructKind.Override })
4547
{
4648
ctx.Code.AppendLine($"var {perResolveVar.VariableName}Created = false;");
4749
}

src/Pure.DI.Core/Core/Code/CompositionClassBuilder.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ public CompositionCode Build(CompositionCode composition)
2222
if (composition.Compilation.Options.NullableContextOptions != NullableContextOptions.Disable)
2323
{
2424
code.AppendLine("#nullable enable annotations");
25-
code.AppendLine("#pragma warning disable CS0219");
2625
}
2726

2827
code.AppendLine();

src/Pure.DI.Core/Core/Code/IVariablesMap.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,5 @@ interface IVariablesMap : IDictionary<MdBinding, Variable>
88

99
IEnumerable<Variable> GetSingletons();
1010

11-
1211
IEnumerable<Variable> GetPerResolves();
1312
}

src/Pure.DI.Core/Core/ConstructDependencyNodeBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public IEnumerable<DependencyNode> Build(DependencyNodeBuildContext ctx)
3131
tag));
3232
}
3333

34-
yield return new DependencyNode(0, binding, Construct: new DpConstruct(construct, binding, injections.ToImmutableArray()));
34+
yield return new DependencyNode(0, binding, ctx.TypeConstructor, Construct: new DpConstruct(construct, binding, injections.ToImmutableArray()));
3535
}
3636
}
3737
}

src/Pure.DI.Core/Core/DependencyGraphBuilder.cs

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ public IEnumerable<DependencyNode> TryBuild(
125125
when namedTypeSymbol.IsGenericType || marker.IsMarkerBased(setup, namedTypeSymbol):
126126
{
127127
var isDone = false;
128-
foreach (var item in map)
128+
foreach (var item in map.OrderByDescending(i => i.Value.Binding.Id))
129129
{
130130
if (item.Key.Type is not INamedTypeSymbol nextNamedTypeSymbol)
131131
{
@@ -349,7 +349,7 @@ public IEnumerable<DependencyNode> TryBuild(
349349
var injection = injectionInfo.Injection;
350350
var dependency = map.TryGetValue(injection, out var sourceNode)
351351
? new Dependency(true, sourceNode, injection, node.Node)
352-
: new Dependency(false, new DependencyNode(0, node.Node.Binding), injection, node.Node);
352+
: new Dependency(false, new DependencyNode(0, node.Node.Binding, node.Node.TypeConstructor), injection, node.Node);
353353

354354
edges.Add(dependency);
355355
}
@@ -363,8 +363,7 @@ public IEnumerable<DependencyNode> TryBuild(
363363
var overrideMap = new Dictionary<Injection, DependencyNode>();
364364
foreach (var rootNode in from node in graph.Vertices where node.Root is not null select node)
365365
{
366-
var typeConstructor = typeConstructorFactory();
367-
Override(processedNodes, [], overrideMap, setup, graph, rootNode, ref maxId, typeConstructor, overridingEntries);
366+
Override(processedNodes, [], overrideMap, setup, graph, rootNode, ref maxId, overridingEntries);
368367
}
369368

370369
if (overridingEntries.Count > 0)
@@ -416,7 +415,6 @@ private void Override(HashSet<DependencyNode> processedNodes,
416415
Graph<DependencyNode, Dependency> graph,
417416
DependencyNode targetNode,
418417
ref int maxId,
419-
ITypeConstructor typeConstructor,
420418
List<GraphEntry<DependencyNode, Dependency>> overridingEntries)
421419
{
422420
if (!processedNodes.Add(targetNode))
@@ -429,29 +427,40 @@ private void Override(HashSet<DependencyNode> processedNodes,
429427
return;
430428
}
431429

430+
var typeConstructor = targetNode.TypeConstructor;
432431
if (targetNode.Factory is {} factory)
433432
{
434433
foreach (var resolver in factory.Resolvers)
435434
{
436-
//typeConstructor.Construct(setup, )
437435
foreach (var @override in resolver.Overrides)
438436
{
439-
overriddenInjections.Add(@override.Injection);
440-
if (!overrideMap.TryGetValue(@override.Injection, out _))
437+
if (@override.Injections.IsDefaultOrEmpty)
441438
{
442-
var overrideBinding = CreateConstructBinding(
443-
setup,
444-
targetNode,
445-
@override.Injection,
446-
@override.Source.ContractType,
447-
Lifetime.PerResolve,
448-
++maxId,
449-
MdConstructKind.Override,
450-
state: @override);
439+
continue;
440+
}
451441

452-
foreach (var sourceNode in CreateNodes(setup, typeConstructorFactory(), overrideBinding))
442+
MdBinding? overrideBinding = null;
443+
foreach (var overrideInjection in @override.Injections)
444+
{
445+
var injection = overrideInjection with {Type = typeConstructor.Construct(setup, overrideInjection.Type)};
446+
var contractType = typeConstructor.Construct(setup, @override.Source.ContractType);
447+
overriddenInjections.Add(injection);
448+
if (!overrideMap.TryGetValue(injection, out _))
453449
{
454-
overrideMap[@override.Injection] = sourceNode;
450+
overrideBinding ??= CreateConstructBinding(
451+
setup,
452+
targetNode,
453+
injection,
454+
contractType,
455+
Lifetime.PerResolve,
456+
++maxId,
457+
MdConstructKind.Override,
458+
state: @override);
459+
460+
foreach (var sourceNode in CreateNodes(setup, typeConstructor, overrideBinding))
461+
{
462+
overrideMap[injection] = sourceNode;
463+
}
455464
}
456465
}
457466
}
@@ -464,7 +473,7 @@ private void Override(HashSet<DependencyNode> processedNodes,
464473
{
465474
if (!overriddenInjections.Contains(dependency.Injection) || !overrideMap.TryGetValue(dependency.Injection, out var overridingSourceNode))
466475
{
467-
Override(processedNodes, overriddenInjections, overrideMap, setup, graph, dependency.Source, ref maxId, typeConstructor, overridingEntries);
476+
Override(processedNodes, overriddenInjections, overrideMap, setup, graph, dependency.Source, ref maxId, overridingEntries);
468477
newDependencies.Add(dependency);
469478
continue;
470479
}

src/Pure.DI.Core/Core/FactoryApiWalker.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ when invocation.ArgumentList.Arguments.Count is 1 or 2
3636
break;
3737

3838
case nameof(IContext.Override)
39-
when invocation.ArgumentList.Arguments.Count is 1 or 2
39+
when invocation.ArgumentList.Arguments.Count > 0
4040
&& memberAccess is { Expression: IdentifierNameSyntax contextIdentifierName }
4141
&& contextIdentifierName.IsKind(SyntaxKind.IdentifierName):
4242
_overrides.Add(new OverrideMeta(_position++, invocation));
@@ -63,7 +63,7 @@ when invocation.ArgumentList.Arguments.Count is 1 or 2
6363
break;
6464

6565
case nameof(IContext.Override)
66-
when invocation.ArgumentList.Arguments.Count is 1 or 2
66+
when invocation.ArgumentList.Arguments.Count > 0
6767
&& memberAccess is { Expression: IdentifierNameSyntax contextIdentifierName }
6868
&& contextIdentifierName.IsKind(SyntaxKind.IdentifierName):
6969
_overrides.Add(new OverrideMeta(_position++, invocation));

src/Pure.DI.Core/Core/FactoryDependencyNodeBuilder.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,14 @@ public IEnumerable<DependencyNode> Build(DependencyNodeBuildContext ctx)
3939
}
4040

4141
var dpFactory = new DpFactory(factory, binding, resolvers.ToImmutableArray(), initializers.ToImmutableArray());
42-
yield return new DependencyNode(0, binding, Factory: dpFactory);
42+
yield return new DependencyNode(0, binding, ctx.TypeConstructor, Factory: dpFactory);
4343
}
4444
}
4545

4646
private static ImmutableArray<DpOverride> CreateOverrides(in ImmutableArray<MdOverride> overrides) =>
4747
overrides.IsDefault
4848
? ImmutableArray<DpOverride>.Empty
4949
: overrides.AsEnumerable()
50-
.Select(i => new DpOverride(i, new Injection(InjectionKind.Override, i.ContractType, i.Tag?.Value)))
50+
.Select(i => new DpOverride(i, i.Tags.Select(tag => new Injection(InjectionKind.Override, i.ContractType, tag.Value)).ToImmutableArray()))
5151
.ToImmutableArray();
5252
}

0 commit comments

Comments
 (0)