Skip to content

Commit 87792b3

Browse files
committed
exposing the Request.SelectedConstuctor to support user-level checking constuctor when injecting the properties for #563
1 parent e244b33 commit 87792b3

File tree

3 files changed

+126
-41
lines changed

3 files changed

+126
-41
lines changed

src/DryIoc/Container.cs

+54-40
Original file line numberDiff line numberDiff line change
@@ -8637,9 +8637,12 @@ public static T New<T>(this IResolver resolver, Made.TypedMade<T> made,
86378637
public static Expression CreateResolutionExpression(Request request,
86388638
bool openResolutionScope = false, bool asResolutionCall = false)
86398639
{
8640-
if (request.Rules.DependencyResolutionCallExprs != null &&
8641-
request.Factory != null && !request.Factory.HasRuntimeState)
8642-
PopulateDependencyResolutionCallExpressions(request);
8640+
if (request.Rules.DependencyResolutionCallExprs != null)
8641+
{
8642+
var f = request.Factory;
8643+
if (f != null && !f.HasRuntimeState)
8644+
PopulateDependencyResolutionCallExpressions(request);
8645+
}
86438646

86448647
var container = request.Container;
86458648
var serviceType = request.ServiceType;
@@ -9466,18 +9469,18 @@ public static Request Create(IContainer container, ServiceInfo serviceInfo,
94669469

94679470
// inherit some flags and service details from parent (if any)
94689471
preResolveParent = preResolveParent ?? Empty;
9469-
if (!preResolveParent.IsEmpty)
9472+
if (preResolveParent.IsEmpty)
9473+
flags |= preResolveParent.Flags; //inherits the OpensResolutionScope flag in case of Request.EmptyOpensResolutionScope
9474+
else
94709475
{
94719476
var parentServiceInfo = preResolveParent.ServiceTypeOrInfo;
94729477
if (parentServiceInfo is ServiceInfo ps && ps.Details != null && ps.Details != ServiceDetails.Default)
94739478
serviceInfo = serviceInfo.InheritInfoFromDependencyOwner(ps.ServiceType, ps.Details, container, preResolveParent.FactoryType);
94749479

9475-
flags |= preResolveParent.Flags & InheritedFlags;
9480+
flags |= preResolveParent.Flags & InheritedFlags; // filter out flags which are not inherited
94769481
}
9477-
else
9478-
flags |= preResolveParent.Flags; //inherits the OpensResolutionScope flag
94799482

9480-
var inputArgExprs = inputArgs?.Map(a => Constant(a)); // todo: @check what happens if `a == null`, does the `object` type for is fine
9483+
var inputArgExprs = inputArgs?.Map(static a => Constant(a)); // todo: @check what happens if `a == null`, does the `object` type for is fine
94819484

94829485
// we are re-starting the dependency depth count from `1`
94839486
var stack = RequestStack.Create();
@@ -9513,64 +9516,68 @@ public static Request Create(IContainer container, Type serviceType,
95139516
Request preResolveParent = null, RequestFlags flags = default, object[] inputArgs = null) =>
95149517
Create(container, ServiceInfo.Of(serviceType, requiredServiceType, ifUnresolved, serviceKey), preResolveParent, flags, inputArgs);
95159518

9516-
/// <summary>Available in runtime only, provides access to container initiated the request.</summary>
9519+
/// <summary>Available at runtime only, provides an access to container initiated the request.</summary>
95179520
public IContainer Container { get; private set; }
95189521

95199522
/// <summary>Request immediate parent.</summary>
95209523
public Request DirectParent;
95219524

95229525
internal RequestStack RequestStack;
95239526

9524-
// mutable because of RequestFlags.AddedToResolutionExpressions
9525-
/// <summary>Persisted request conditions</summary>
9526-
public RequestFlags Flags; // todo: @perf combine with the FactoryType or other numeric fields
9527-
9528-
// todo: @perf should we unpack the info to the ServiceType and Details (or at least the Details), because we are accessing them via Virtual Calls (and it is a lot)
9529-
// The field is mutable so that the ServiceKey or IfUnresolved can be changed in place.
9527+
// todo: @perf should we unpack the info to the ServiceType and Details (or at least the Details), because we are accessing them via virtual calls (and there are a lot)
9528+
// The field is mutable so that the service details ServiceKey or IfUnresolved can be changed in place.
95309529
internal object ServiceTypeOrInfo; // the Type or the ServiceInfo
9530+
private Type _actualServiceType; // the actual service type may in fact be the same as ServiceTypeOrInfo
95319531

95329532
/// <summary>Input arguments provided with `Resolve`</summary>
95339533
internal Expression[] InputArgExprs;
95349534

9535-
/// <summary>Runtime known resolve factory, otherwise is <c>null</c></summary>
9536-
internal Factory Factory => _factoryOrImplType as Factory;
9535+
/// <summary>Service reuse.</summary>
9536+
public IReuse Reuse { get; private set; }
9537+
9538+
internal object _factoryOrImplType;
9539+
9540+
/// <summary>Constructor selected by the reflection factory</summary>
9541+
public ConstructorInfo SelectedConstructor { get; internal set; }
95379542

95389543
/// <summary>Resolved factory ID, used to identify applied decorator.</summary>
95399544
public int FactoryID { get; private set; }
95409545

9546+
/// <summary>ID of decorated factory in case of decorator factory type</summary>
9547+
public int DecoratedFactoryID { get; private set; } // todo: @perf can we remove or combine it with the other fields?
9548+
95419549
// based on the parent(s) and current request FactoryID
95429550
private int _hashCode; // todo: @perf do we need to calculate and store the hash code if it is not used
95439551

95449552
/// <summary>Type of factory: Service, Wrapper, or Decorator.</summary>
95459553
public FactoryType FactoryType { get; private set; }
95469554

9555+
// mutable because of RequestFlags.AddedToResolutionExpressions
9556+
/// <summary>Persisted request conditions</summary>
9557+
public RequestFlags Flags; // todo: @perf combine with the FactoryType or other numeric fields
9558+
9559+
/// <summary>Number of nested dependencies. Set with each new Push.</summary>
9560+
public int DependencyDepth; // todo: @perf combine with the DependencyCount or other fields, use the ObjectLayoutInspector to check the layout
9561+
9562+
/// <summary>The total dependency count</summary>
9563+
public int DependencyCount; // todo: @perf combine with the DependencyDepth or other fields
9564+
95479565
/// <summary>Combines decorator and <see cref="DecoratedFactoryID"/></summary>
95489566
public int CombineDecoratorWithDecoratedFactoryID() => FactoryID | (DecoratedFactoryID << 16);
95499567

9550-
/// <summary>Service implementation type if known.</summary>
9551-
public Type ImplementationType => _factoryOrImplType as Type ?? Factory?.ImplementationType;
9552-
9553-
internal object _factoryOrImplType;
9568+
/// <summary>Runtime known resolve factory, otherwise is <c>null</c></summary>
9569+
internal Factory Factory => _factoryOrImplType as Factory;
95549570

9571+
/// <summary>Service implementation type if known.</summary>
9572+
public Type ImplementationType => _factoryOrImplType as Type ?? (_factoryOrImplType as Factory)?.ImplementationType;
9573+
95559574
/// <summary>Sets the service factory already resolved by the wrapper to save for the future factory resolution</summary>
95569575
public Request WithWrappedServiceFactory(Factory f)
95579576
{
95589577
_factoryOrImplType = f;
95599578
return this;
95609579
}
95619580

9562-
/// <summary>Service reuse.</summary>
9563-
public IReuse Reuse { get; private set; }
9564-
9565-
/// <summary>ID of decorated factory in case of decorator factory type</summary>
9566-
public int DecoratedFactoryID { get; private set; } // todo: @perf can we remove or combine it with the other fields?
9567-
9568-
/// <summary>Number of nested dependencies. Set with each new Push.</summary>
9569-
public int DependencyDepth; // todo: @perf combine with theDependencyCount or other fields, use the ObjectLayoutInspector to check the layout
9570-
9571-
/// <summary>The total dependency count</summary>
9572-
public int DependencyCount; // todo: @perf combine with the DependencyDepth or other fields
9573-
95749581
internal void DecreaseTrackedDependencyCountForParents(int dependencyCount)
95759582
{
95769583
for (var p = DirectParent; !p.IsEmpty; p = p.DirectParent)
@@ -9642,7 +9649,6 @@ public Request Parent
96429649

96439650
/// <summary>Compatible required or service type.</summary>
96449651
public Type GetActualServiceType() => _actualServiceType;
9645-
private Type _actualServiceType;
96469652

96479653
/// <summary>Get the details</summary>
96489654
public ServiceDetails GetServiceDetails() => ServiceTypeOrInfo is ServiceInfo i ? i.Details : ServiceDetails.Default;
@@ -9660,7 +9666,7 @@ public Request Parent
96609666
public int ReuseLifespan => Reuse?.Lifespan ?? 0;
96619667

96629668
/// <summary>Known implementation, or otherwise actual service type.</summary>
9663-
public Type GetKnownImplementationOrServiceType() => _factoryOrImplType as Type ?? Factory?.ImplementationType ?? _actualServiceType;
9669+
public Type GetKnownImplementationOrServiceType() => ImplementationType ?? _actualServiceType;
96649670

96659671
private ref Request GetOrPushPooledRequest(RequestStack stack, int indexInStack)
96669672
{
@@ -10120,8 +10126,9 @@ public StringBuilder PrintCurrent(StringBuilder s = null)
1012010126

1012110127
s.Append(ServiceTypeOrInfo is ParameterInfo pi ? ParameterServiceInfo.Of(pi) : ServiceTypeOrInfo);
1012210128

10123-
if (Factory != null && Factory is ReflectionFactory == false)
10124-
s.Append(' ').Append(Factory.GetType().Name).Append(' ');
10129+
var f = Factory;
10130+
if (f != null && f is ReflectionFactory == false)
10131+
s.Append(' ').Append(f.GetType().Name).Append(' ');
1012510132

1012610133
if (FactoryID != 0)
1012710134
s.Append(" FactoryId=").Append(FactoryID);
@@ -10250,6 +10257,7 @@ private void SetServiceInfo(IContainer container, Request parent, int dependency
1025010257
Flags = flags;
1025110258
// resets the factory info:
1025210259
_factoryOrImplType = null;
10260+
SelectedConstructor = null;
1025310261
Reuse = null;
1025410262
FactoryID = 0;
1025510263
DecoratedFactoryID = 0;
@@ -10260,6 +10268,7 @@ private void SetServiceInfo(IContainer container, Request parent, int dependency
1026010268
private void SetResolvedFactory(object factoryOrImplType, int factoryID, FactoryType factoryType, IReuse reuse, int decoratedFactoryID)
1026110269
{
1026210270
_factoryOrImplType = factoryOrImplType;
10271+
SelectedConstructor = null;
1026310272
FactoryID = factoryID;
1026410273
FactoryType = factoryType;
1026510274
Reuse = reuse;
@@ -11830,14 +11839,17 @@ public override Expression CreateExpressionOrDefault(Request request)
1183011839
var ctorOrMember = factoryMethod.ConstructorOrMethodOrMember;
1183111840
if (factoryMethod.ResolvedParameterExpressions != null)
1183211841
{
11842+
ctor = (ConstructorInfo)ctorOrMember;
11843+
request.SelectedConstructor = ctor;
11844+
1183311845
if (rules.UsedForValidation)
1183411846
{
1183511847
TryGetMemberAssignments(ref failedToGetMember, request, container, rules);
1183611848
return request.GetActualServiceType().GetDefaultValueExpression();
1183711849
}
1183811850

1183911851
var assignements = TryGetMemberAssignments(ref failedToGetMember, request, container, rules);
11840-
var newExpr = New((ConstructorInfo)ctorOrMember, factoryMethod.ResolvedParameterExpressions);
11852+
var newExpr = New(ctor, factoryMethod.ResolvedParameterExpressions);
1184111853
return failedToGetMember ? null : assignements == null ? newExpr : (Expression)MemberInit(newExpr, assignements);
1184211854
}
1184311855

@@ -11846,10 +11858,12 @@ public override Expression CreateExpressionOrDefault(Request request)
1184611858
return ConvertExpressionIfNeeded(
1184711859
ctorOrMember is PropertyInfo p ? Property(factoryExpr, p) : Field(factoryExpr, (FieldInfo)ctorOrMember), request, ctorOrMember);
1184811860

11849-
ctor = ctorOrMember as ConstructorInfo;
11850-
method = ctorOrMember as MethodInfo;
11861+
ctor = ctorOrMethod as ConstructorInfo;
11862+
method = ctorOrMethod as MethodInfo;
1185111863
}
1185211864

11865+
request.SelectedConstructor = ctor;
11866+
1185311867
var parameters = ctorOrMethod.GetParameters();
1185411868
if (parameters.Length == 0)
1185511869
{

test/DryIoc.TestRunner/Program.cs

+4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ public static void Main()
1010
{
1111
RunAllTests();
1212

13+
// new PropertyResolutionTests().Run();
14+
15+
// new GHIssue391_Deadlock_during_Resolve().Run();
1316
// new GHIssue559_Possible_inconsistent_behaviour().Run();
1417
// new GHIssue557_WithFactorySelector_allows_to_Resolve_the_keyed_service_as_non_keyed().Run();
1518
// new GHIssue555_ConcreteTypeDynamicRegistrations_is_not_working_with_MicrosoftDependencyInjectionRules().Run();
@@ -52,6 +55,7 @@ void Run(Func<int> run, string name = null)
5255
new ContainerTests(),
5356
new OpenGenericsTests(),
5457
new DynamicRegistrationsTests(),
58+
new PropertyResolutionTests(),
5559
new SelectConstructorWithAllResolvableArgumentTests(),
5660
new Issue107_NamedScopesDependingOnResolvedTypes(),
5761
new GHIssue378_InconsistentResolutionFailure(),

test/DryIoc.UnitTests/PropertyResolutionTests.cs

+68-1
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,14 @@
77
namespace DryIoc.UnitTests
88
{
99
[TestFixture]
10-
public class PropertyResolutionTests
10+
public class PropertyResolutionTests : ITest
1111
{
12+
public int Run()
13+
{
14+
Can_access_the_constructor_when_resolving_the_property();
15+
return 1;
16+
}
17+
1218
[Test]
1319
public void Resolving_unregistered_property_should_NOT_throw_and_should_preserve_original_property_value()
1420
{
@@ -162,6 +168,49 @@ private static PropertyOrFieldServiceInfo GetImportedPropertiesAndFields(MemberI
162168
return import == null ? null : PropertyOrFieldServiceInfo.Of(m)
163169
.WithDetails(ServiceDetails.Of(import.ContractType, import.ContractName));
164170
}
171+
172+
[Test]
173+
public void Can_access_the_constructor_when_resolving_the_property()
174+
{
175+
var container = new Container(r => r.With(
176+
factoryMethod: FactoryMethod.ConstructorWithResolvableArguments,
177+
propertiesAndFields: PropertiesAndFields.All(
178+
withFields: false,
179+
serviceInfo: (m, req) =>
180+
{
181+
var import = (ImportAttribute)m.GetAttributes(typeof(ImportAttribute)).FirstOrDefault();
182+
if (import == null)
183+
return null;
184+
185+
// if the used constructor already contains (the injected) parameter of the same type as the property,
186+
// then we skip the property injection.
187+
// In C# 11 (.NET 7) it may be used to determine the constructor.SetsRequiredMembers to avoid injection of the required properties, #563
188+
var ctor = req.SelectedConstructor;
189+
if (ctor != null)
190+
{
191+
var ctorParams = ctor.GetParameters();
192+
if (ctorParams.Length != 0)
193+
{
194+
var propType = m.GetReturnTypeOrDefault();
195+
foreach (var p in ctorParams)
196+
{
197+
if (p.ParameterType == propType)
198+
return null;
199+
}
200+
}
201+
}
202+
203+
return PropertyOrFieldServiceInfo.Of(m).WithDetails(ServiceDetails.Of(import.ContractType, import.ContractName));
204+
})));
205+
206+
container.Register<Cuerpo>();
207+
container.Register<Guts>();
208+
container.Register<Brain>();
209+
210+
var cuerpo = container.Resolve<Cuerpo>();
211+
212+
Assert.IsNotNull(cuerpo.Brain);
213+
}
165214
}
166215

167216
#region CUT
@@ -242,5 +291,23 @@ public class Brain
242291
{
243292
}
244293

294+
public class Cuerpo
295+
{
296+
[Import]
297+
public Brain Brain { get; set; }
298+
299+
public readonly Guts Guts;
300+
public Cuerpo(Guts guts)
301+
{
302+
Guts = guts;
303+
}
304+
305+
public Cuerpo(Guts guts, Brain brain)
306+
{
307+
Guts = guts;
308+
Brain = brain;
309+
}
310+
}
311+
245312
#endregion
246313
}

0 commit comments

Comments
 (0)