Skip to content

Commit fd04c75

Browse files
add support for inheriting fromkeyedservices value for the constraint resolution
1 parent 87ea24c commit fd04c75

File tree

3 files changed

+71
-78
lines changed

3 files changed

+71
-78
lines changed
Lines changed: 64 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Linq;
34
using System.Reflection;
45
using Microsoft.Extensions.DependencyInjection;
56
using Ninject.Activation;
7+
using Ninject.Parameters;
68
using Ninject.Planning.Bindings;
79
using Ninject.Planning.Targets;
810
using Ninject.Web.AspNetCore.Parameters;
@@ -32,102 +34,90 @@ public override bool HasDefaultValue
3234
}
3335
}
3436

35-
protected override Func<IBindingMetadata, bool> ReadConstraintFromTarget()
37+
/// <summary>
38+
/// MethodInjectionStrategy.GetMethodArguments calls ITarget.ResolveWithin.
39+
/// As we can't override the base implementation as it is not virtual, the
40+
/// explicit interface implementation helps to still delegate the resolution to here.
41+
/// </summary>
42+
object ITarget.ResolveWithin(IContext parent)
3643
{
3744
#if NET8_0_OR_GREATER
38-
var keyedattributes = GetCustomAttributes(typeof (FromKeyedServicesAttribute), true) as FromKeyedServicesAttribute[];
39-
var baseFunc = base.ReadConstraintFromTarget();
40-
if (keyedattributes == null || keyedattributes.Length == 0
41-
#if NET10_0_OR_GREATER
42-
|| (keyedattributes[0].LookupMode == ServiceKeyLookupMode.NullKey)
43-
#endif
44-
)
45+
var serviceKeyAttributes = GetCustomAttributes(typeof (ServiceKeyAttribute), true) as ServiceKeyAttribute[];
46+
if (serviceKeyAttributes?.Length > 0)
4547
{
46-
return baseFunc;
48+
return ResolveServiceKeyValue(parent);
4749
}
4850

49-
return metadata =>
51+
var keyedattributes = GetCustomAttributes(typeof (FromKeyedServicesAttribute), true) as FromKeyedServicesAttribute[];
52+
if (keyedattributes?.Length > 0)
5053
{
51-
var result = true;
52-
if (baseFunc != null)
53-
{
54-
result = baseFunc(metadata);
55-
}
56-
57-
if (metadata.HasServiceKeyMetadata())
58-
{
59-
object keyToCompareWith = keyedattributes[0].Key;
60-
#if NET10_0_OR_GREATER
61-
if (keyedattributes[0].LookupMode == ServiceKeyLookupMode.InheritKey) {
62-
// here we would need the request!
63-
}
54+
return ResolveFromKeyedService(parent, keyedattributes[0]);
55+
}
6456
#endif
65-
result = result && metadata.DoesMetadataMatchServiceKey(keyToCompareWith);
66-
}
67-
else
68-
{
69-
// we can't match bindings here which don't have a servicekey. If FromKeyServiceAttribute is present
70-
// the match fails if no servicekey available.
71-
result = false;
72-
}
57+
return base.ResolveWithin(parent);
58+
}
7359

74-
return result;
75-
};
76-
#else
77-
return base.ReadConstraintFromTarget();
60+
#if NET8_0_OR_GREATER
61+
private object ResolveFromKeyedService(IContext parent, FromKeyedServicesAttribute keyedattribute)
62+
{
63+
var fromKeyedServiceValue = DeterimeFromKeyedServiceValue(keyedattribute, parent.Parameters);
64+
var additionalConstraint = fromKeyedServiceValue != null
65+
? metadata => metadata.DoesMetadataMatchServiceKey(fromKeyedServiceValue)
66+
: (Func<IBindingMetadata, bool>) null;
67+
var child = parent.Request.CreateKeyedChildRequest(Type, fromKeyedServiceValue, parent, this, additionalConstraint);
68+
child.IsUnique = true;
69+
return parent.Kernel.Resolve(child).SingleOrDefault();
70+
}
71+
72+
private object DeterimeFromKeyedServiceValue(
73+
FromKeyedServicesAttribute keyedattribute, ICollection<IParameter> parameters)
74+
{
75+
#if NET10_0_OR_GREATER
76+
if (keyedattribute.LookupMode == ServiceKeyLookupMode.NullKey)
77+
{
78+
// means no constraint, resolve normally.
79+
return null;
80+
}
81+
if (keyedattribute.LookupMode == ServiceKeyLookupMode.InheritKey)
82+
{
83+
var serviceKeyParam = parameters.LastOrDefault(x => x is ServiceKeyParameter) as ServiceKeyParameter;
84+
return serviceKeyParam?.ServiceKey;
85+
}
7886
#endif
87+
return keyedattribute.Key;
7988
}
8089

81-
/// <summary>
82-
/// MethodInjectionStrategy.GetMethodArguments calls ITarget.ResolveWithin.
83-
/// As we can't override the base implementation as it is not virtual, the
84-
/// explicit interface implementation helps to still delegate the resolution to here.
85-
/// </summary>
86-
object ITarget.ResolveWithin(IContext parent)
90+
private object ResolveServiceKeyValue(IContext parent)
8791
{
88-
#if NET8_0_OR_GREATER
89-
var serviceKeyAttributes = GetCustomAttributes(typeof (ServiceKeyAttribute), true) as ServiceKeyAttribute[];
90-
if (serviceKeyAttributes?.Length > 0)
92+
var result = parent.Binding.Metadata.GetServiceKey();
93+
var serviceKeyParameter = parent.Parameters.LastOrDefault(x => x is ServiceKeyParameter) as ServiceKeyParameter;
94+
if (serviceKeyParameter != null)
9195
{
92-
var result = parent.Binding.Metadata.GetServiceKey();
93-
var serviceKeyParameter = parent.Parameters.LastOrDefault(x => x is ServiceKeyParameter) as ServiceKeyParameter;
94-
if (serviceKeyParameter != null)
95-
{
96-
result = serviceKeyParameter.ServiceKey;
97-
}
96+
result = serviceKeyParameter.ServiceKey;
97+
}
9898

99-
var asConvertible = result as IConvertible;
100-
if (asConvertible != null)
99+
var asConvertible = result as IConvertible;
100+
if (asConvertible != null)
101+
{
102+
try
101103
{
102-
try
103-
{
104-
result = Convert.ChangeType(asConvertible, this.Type);
105-
}
106-
catch (InvalidCastException)
107-
{
108-
// we have to throw and InvalidOperationException in this case, a InvalidCastException
109-
// is not passing the tests
110-
throw new InvalidOperationException("Cannot convert " + asConvertible + " to " + this.Type);
111-
}
104+
result = Convert.ChangeType(asConvertible, this.Type);
112105
}
113-
114-
if (result != null && !this.Type.IsAssignableFrom(result.GetType()))
106+
catch (InvalidCastException)
115107
{
116-
throw new InvalidOperationException("Cannot convert " + result + " to " + this.Type);
108+
// we have to throw and InvalidOperationException in this case, a InvalidCastException
109+
// is not passing the tests
110+
throw new InvalidOperationException("Cannot convert " + asConvertible + " to " + this.Type);
117111
}
118-
119-
return result;
120112
}
121113

122-
var keyedattributes = GetCustomAttributes(typeof (FromKeyedServicesAttribute), true) as FromKeyedServicesAttribute[];
123-
if (keyedattributes?.Length > 0)
114+
if (result != null && !this.Type.IsAssignableFrom(result.GetType()))
124115
{
125-
var child = parent.Request.CreateKeyedChildRequest(Type, keyedattributes[0].Key, parent, this);
126-
child.IsUnique = true;
127-
return parent.Kernel.Resolve(child).SingleOrDefault();
116+
throw new InvalidOperationException("Cannot convert " + result + " to " + this.Type);
128117
}
129-
#endif
130-
return base.ResolveWithin(parent);
118+
119+
return result;
131120
}
121+
#endif
132122
}
133123
}

src/Ninject.Web.AspNetCore/RequestActivation/KeyedRequest.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,15 @@ public KeyedRequest(Type service, object serviceKey, Func<IBindingMetadata, bool
4444
/// <param name="service">The service that was requested.</param>
4545
/// <param name="target">The target that will receive the injection.</param>
4646
/// <param name="scopeCallback">The scope callback, if an external scope was specified.</param>
47-
public KeyedRequest(IContext parentContext, Type service, object serviceKey, ITarget target, Func<object> scopeCallback)
47+
public KeyedRequest(IContext parentContext, Type service, object serviceKey, ITarget target, Func<object> scopeCallback, Func<IBindingMetadata, bool> additionalConstraint = null)
4848
{
4949
ParentContext = parentContext;
5050
ParentRequest = parentContext.Request;
5151
Service = service;
5252
Target = target;
53-
Constraint = target.Constraint;
53+
Constraint = additionalConstraint == null
54+
? target.Constraint
55+
: metadata => (target.Constraint?.Invoke(metadata) ?? true) && additionalConstraint(metadata);
5456
IsOptional = target.IsOptional;
5557
Parameters = new List<IParameter>(parentContext.Parameters.Where(x => x.ShouldInherit));
5658
var parametersToRemove = Parameters.Where(x => x is ServiceKeyParameter).ToList();

src/Ninject.Web.AspNetCore/RequestActivation/KeyedRequestExtensions.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
using System;
22
using System.Linq;
33
using Ninject.Activation;
4+
using Ninject.Planning.Bindings;
45
using Ninject.Planning.Targets;
56

67
namespace Ninject.Web.AspNetCore.RequestActivation;
78

89
public static class KeyedRequestExtensions
910
{
1011
public static IRequest CreateKeyedChildRequest(this IRequest parentRequest, Type service, object serviceKey,
11-
IContext parentContext, ITarget target)
12+
IContext parentContext, ITarget target, Func<IBindingMetadata, bool> additionalConstraint = null)
1213
{
13-
return new KeyedRequest(parentContext, service, serviceKey, target, parentRequest.GetScope);
14+
return new KeyedRequest(parentContext, service, serviceKey, target, parentRequest.GetScope, additionalConstraint);
1415
}
1516

1617
public static IRequest ToKeyedRequest(this IRequest request, object serviceKey)

0 commit comments

Comments
 (0)