Skip to content

Commit 13f268b

Browse files
fix binding overriding with keyed services
1 parent baf79ef commit 13f268b

File tree

6 files changed

+61
-15
lines changed

6 files changed

+61
-15
lines changed

src/Ninject.Web.AspNetCore.Test/Unit/IndexedBindingPrecedenceComparerTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ public DummyBinding()
8686

8787
public DummyBinding WithIndex(BindingIndex index)
8888
{
89-
Metadata.Set(nameof(BindingIndex), index.Next(Service));
89+
Metadata.Set(nameof(BindingIndex), index.Next(Service, BindingIndex.DefaultIndexKey));
9090
return this;
9191
}
9292

src/Ninject.Web.AspNetCore.Test/Unit/ServiceProviderKeyedTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ public void ExistingMultipleServices_ResolvesNonKeyedToException()
203203
var provider = CreateServiceProvider(kernel);
204204

205205
Action action = () => provider.GetRequiredService(typeof(IWarrior));
206-
action.Should().Throw<ActivationException>().WithMessage("*More than one matching bindings are available*");
206+
action.Should().Throw<ActivationException>().WithMessage("*No matching bindings are available, and the type is not self-bindable.*");
207207
}
208208

209209
private IServiceProvider CreateServiceProvider(AspNetCoreKernel kernel)

src/Ninject.Web.AspNetCore/AspNetCoreKernel.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ protected override Func<IBinding, bool> SatifiesRequest(IRequest request)
3636
var latest = true;
3737
if (request.IsUnique)
3838
{
39+
// as we can't register constraints via microsoft.extensions.dependencyinjection,
40+
// we always check for the latest binding
41+
// Note that we have at least one constraint for the servicekey >= .NET 8.0
3942
latest = binding.Metadata.Get<BindingIndex.Item>(nameof(BindingIndex))?.IsLatest ?? true;
4043
}
4144
return binding.Matches(request) && request.Matches(binding) && latest;

src/Ninject.Web.AspNetCore/BindingIndex.cs

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,29 @@ namespace Ninject.Web.AspNetCore
55
{
66
public class BindingIndex
77
{
8-
private readonly IDictionary<Type, Item> _bindingIndexMap = new Dictionary<Type, Item>();
8+
private readonly IDictionary<ServiceTypeKey, Item> _bindingIndexMap = new Dictionary<ServiceTypeKey, Item>();
9+
public const string DefaultIndexKey = "NonKeyed";
910

1011
public int Count { get; private set; }
1112

1213
public BindingIndex()
1314
{
1415
}
1516

16-
public Item Next(Type serviceType)
17+
public Item Next(Type serviceType, object indexKey)
1718
{
18-
19-
_bindingIndexMap.TryGetValue(serviceType, out var previous);
19+
var serviceTypeKey = new ServiceTypeKey(serviceType, indexKey);
20+
_bindingIndexMap.TryGetValue(serviceTypeKey, out var previous);
2021

21-
var next = new Item(this, serviceType, Count++, previous?.TypeIndex + 1 ?? 0);
22-
_bindingIndexMap[serviceType] = next;
22+
var next = new Item(this, serviceType, indexKey, Count++, previous?.TypeIndex + 1 ?? 0);
23+
_bindingIndexMap[serviceTypeKey] = next;
2324

2425
return next;
2526
}
2627

27-
private bool IsLatest(Type serviceType, Item item)
28+
private bool IsLatest(Type serviceType, object indexKey, Item item)
2829
{
29-
return _bindingIndexMap[serviceType] == item;
30+
return _bindingIndexMap[new ServiceTypeKey(serviceType, indexKey)] == item;
3031
}
3132

3233
public class Item
@@ -36,16 +37,55 @@ public class Item
3637

3738
public int TotalIndex { get; }
3839
public int TypeIndex { get; }
40+
public object IndexKey { get; }
3941

40-
public bool IsLatest => _root.IsLatest(_serviceType, this);
42+
public bool IsLatest => _root.IsLatest(_serviceType, IndexKey, this);
4143
public int Precedence => _root.Count - TotalIndex;
4244

43-
public Item(BindingIndex root, Type serviceType, int totalIndex, int typeIndex)
45+
public Item(BindingIndex root, Type serviceType, object indexKey, int totalIndex, int typeIndex)
4446
{
4547
_root = root;
4648
_serviceType = serviceType;
4749
TotalIndex = totalIndex;
4850
TypeIndex = typeIndex;
51+
IndexKey = indexKey;
52+
}
53+
}
54+
55+
/// <summary>
56+
/// We have to to separate the precedence by servicekey.
57+
/// This ensures that a binding with a different servicekey
58+
/// can't override a binding with a non-matching servicekey
59+
/// </summary>
60+
public class ServiceTypeKey : IEquatable<ServiceTypeKey>
61+
{
62+
public Type ServiceType { get; }
63+
public object IndexKey { get; }
64+
65+
public ServiceTypeKey(Type serviceType, object indexKey)
66+
{
67+
ServiceType = serviceType;
68+
IndexKey = indexKey;
69+
}
70+
71+
public bool Equals(ServiceTypeKey other)
72+
{
73+
if (other is null) return false;
74+
if (ReferenceEquals(this, other)) return true;
75+
return Equals(ServiceType, other.ServiceType) && Equals(IndexKey, other.IndexKey);
76+
}
77+
78+
public override bool Equals(object obj)
79+
{
80+
if (obj is null) return false;
81+
if (ReferenceEquals(this, obj)) return true;
82+
if (obj.GetType() != GetType()) return false;
83+
return Equals((ServiceTypeKey)obj);
84+
}
85+
86+
public override int GetHashCode()
87+
{
88+
return HashCode.Combine(ServiceType, IndexKey);
4989
}
5090
}
5191
}

src/Ninject.Web.AspNetCore/Planning/ConstructorReflectionStrategyWithKeyedSupport.cs renamed to src/Ninject.Web.AspNetCore/Components/ConstructorReflectionStrategyWithKeyedSupport.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@
77
using Ninject.Planning.Directives;
88
using Ninject.Planning.Strategies;
99
using Ninject.Selection;
10+
using Ninject.Web.AspNetCore.Planning;
1011

11-
namespace Ninject.Web.AspNetCore.Planning
12+
namespace Ninject.Web.AspNetCore.Components
1213
{
1314
/// <summary>
1415
/// Adds a directive to plans indicating which constructor should be injected during activation.

src/Ninject.Web.AspNetCore/ServiceCollectionAdapter.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,17 @@ private IBindingWithOrOnSyntax<T> ConfigureImplementationAndLifecycle<T>(
7272
#endif
7373

7474
var resultWithMetadata = ConfigureImplementationAndLifecycleWithAdapter(bindingToSyntax, adapter)
75-
.WithMetadata(nameof(ServiceDescriptor), descriptor)
76-
.WithMetadata(nameof(BindingIndex), bindingIndex.Next(descriptor.ServiceType));
75+
.WithMetadata(nameof(ServiceDescriptor), descriptor);
7776

77+
object indexKey = BindingIndex.DefaultIndexKey;
7878
#if NET8_0_OR_GREATER
7979
if (descriptor.IsKeyedService)
8080
{
8181
resultWithMetadata = resultWithMetadata.WithMetadata(nameof(ServiceKey), new ServiceKey(descriptor.ServiceKey));
82+
indexKey = descriptor.ServiceKey;
8283
}
8384
#endif
85+
resultWithMetadata = resultWithMetadata.WithMetadata(nameof(BindingIndex), bindingIndex.Next(descriptor.ServiceType, indexKey));
8486
return resultWithMetadata;
8587
}
8688

0 commit comments

Comments
 (0)