Skip to content

Commit 61c9c5a

Browse files
handle special cases with AnyKey as well as with null key
1 parent 13f268b commit 61c9c5a

File tree

6 files changed

+48
-10
lines changed

6 files changed

+48
-10
lines changed

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,19 @@ namespace Ninject.Web.AspNetCore.Test.Unit
1414

1515
public class ServiceProviderKeyedTest
1616
{
17+
18+
[Fact]
19+
public void OptionalExising_ServiceKeyNullResolvedAsUnkeyed()
20+
{
21+
var collection = new ServiceCollection();
22+
collection.Add(new ServiceDescriptor(typeof(IWarrior),null, typeof(Samurai), ServiceLifetime.Transient));
23+
var kernel = CreateTestKernel(collection);
24+
var provider = CreateServiceProvider(kernel);
25+
26+
var warrior = provider.GetKeyedService(typeof(Samurai), null);
27+
warrior.Should().NotBeNull().And.BeOfType(typeof(Samurai));
28+
}
29+
1730
[Fact]
1831
public void OptionalExising_SingleServiceInjectedServiceKeyResolved()
1932
{

src/Ninject.Web.AspNetCore/BindingIndex.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using Microsoft.Extensions.DependencyInjection;
34

45
namespace Ninject.Web.AspNetCore
56
{

src/Ninject.Web.AspNetCore/NinjectServiceProvider.cs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,37 +80,49 @@ public void Dispose()
8080
#if NET8_0_OR_GREATER
8181
public object GetKeyedService(Type serviceType, object serviceKey)
8282
{
83+
if (serviceKey == null)
84+
{
85+
// serviceKey = null means unkeyed
86+
return GetService(serviceType);
87+
}
88+
8389
object result;
8490
if (!IsListType(serviceType, out var elementType))
8591
{
8692
EnsureNotAnyKey(serviceKey, serviceType);
8793
result = _resolutionRoot.TryGet(serviceType,
88-
metadata => metadata.DoesMetadataMatchServiceKey(serviceKey));
94+
metadata => metadata.DoesMetadataMatchServiceKey(serviceKey, true));
8995
}
9096
else
9197
{
9298
// Ninject is not evaluating metadata constraint when resolving a IEnumerable<T>, see KernelBase.UpdateRequest
9399
// Therefore, need to implement a workaround to not instantiate here bindings with a different servicekey value
94100
result = ConvertToTypedEnumerable(elementType,
95-
_resolutionRoot.GetAll(elementType, metadata => metadata.DoesMetadataMatchServiceKey(serviceKey)));
101+
_resolutionRoot.GetAll(elementType, metadata => metadata.DoesMetadataMatchServiceKey(serviceKey, false)));
96102
}
97103

98104
return result;
99105
}
100106

101107
public object GetRequiredKeyedService(Type serviceType, object serviceKey)
102108
{
109+
if (serviceKey == null)
110+
{
111+
// serviceKey = null means unkeyed
112+
return GetRequiredService(serviceType);
113+
}
114+
103115
if (!IsListType(serviceType, out var elementType))
104116
{
105117
EnsureNotAnyKey(serviceKey, serviceType);
106-
return _resolutionRoot.Get(serviceType, metadata => metadata.DoesMetadataMatchServiceKey(serviceKey));
118+
return _resolutionRoot.Get(serviceType, metadata => metadata.DoesMetadataMatchServiceKey(serviceKey, true));
107119
}
108120
else
109121
{
110122
// Ninject is not evaluating metadata constraint when resolving a IEnumerable<T>, see KernelBase.UpdateRequest
111123
// Therefore, need to implement a workaround to not instantiate here bindings with a different servicekey value
112124
return ConvertToTypedEnumerable(elementType,
113-
_resolutionRoot.GetAll(elementType, metadata => metadata.DoesMetadataMatchServiceKey(serviceKey)).ToList());
125+
_resolutionRoot.GetAll(elementType, metadata => metadata.DoesMetadataMatchServiceKey(serviceKey, false)));
114126
}
115127
}
116128

src/Ninject.Web.AspNetCore/NinjectServiceProviderIsService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public bool IsKeyedService(Type serviceType, object serviceKey)
5252
}
5353

5454
return _kernel.CanResolve(serviceType, metadata =>
55-
metadata.DoesMetadataMatchServiceKey(serviceKey));
55+
metadata.DoesMetadataMatchServiceKey(serviceKey, true));
5656
}
5757
#endif
5858
}

src/Ninject.Web.AspNetCore/Planning/KeyedServicesMetaDataExtensions.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,18 @@ public static class KeyedServicesMetaDataExtensions
1212
{
1313

1414
#if NET8_0_OR_GREATER
15-
internal static bool DoesMetadataMatchServiceKey(this IBindingMetadata metadata, object serviceKey)
15+
internal static bool DoesMetadataMatchServiceKey(this IBindingMetadata metadata, object serviceKey, bool isUniqueRequest)
1616
{
1717
if (serviceKey == KeyedService.AnyKey)
1818
{
19-
return HasServiceKeyMetadata(metadata);
19+
// if the service is registered with KeyedService.AnyKey, it must not be returned when querying with AnyKey
20+
// see CombinationalRegistration compliancetest
21+
return HasServiceKeyMetadata(metadata) && !Object.Equals(metadata.GetServiceKey(), KeyedService.AnyKey);
2022
}
21-
return Object.Equals(metadata.GetServiceKey(), serviceKey);
23+
return Object.Equals(metadata.GetServiceKey(), serviceKey)
24+
// if we query with a key different to KeyedService.AnyKey but registired with AnyKey, we have to return it as well
25+
// see ResolveKeyedServiceSingletonInstanceWithAnyKey compliancetest. But only if we resolve a unique instance
26+
|| (isUniqueRequest && Object.Equals(metadata.GetServiceKey(), KeyedService.AnyKey));
2227
}
2328

2429
internal static object GetServiceKey(this IBindingMetadata metadata)

src/Ninject.Web.AspNetCore/Planning/ParameterTargetWithKeyedSupport.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ protected override Func<IBindingMetadata, bool> ReadConstraintFromTarget()
4949

5050
if (metadata.HasServiceKeyMetadata())
5151
{
52-
result = result && metadata.DoesMetadataMatchServiceKey(keyedattributes[0].Key);
52+
result = result && metadata.DoesMetadataMatchServiceKey(keyedattributes[0].Key, true);
5353
}
5454

5555
return result;
@@ -70,7 +70,14 @@ object ITarget.ResolveWithin(IContext parent)
7070
var serviceKeyAttributes = GetCustomAttributes(typeof (ServiceKeyAttribute), true) as ServiceKeyAttribute[];
7171
if (serviceKeyAttributes?.Length > 0)
7272
{
73-
return parent.Binding.Metadata.GetServiceKey();
73+
var result = parent.Binding.Metadata.GetServiceKey();
74+
if (result == KeyedService.AnyKey && this.Type == typeof(string))
75+
{
76+
// expected to automatically convert from AnyKey to string representation
77+
result = KeyedService.AnyKey.ToString();
78+
}
79+
80+
return result;
7481
}
7582
#endif
7683
return base.ResolveWithin(parent);

0 commit comments

Comments
 (0)