diff --git a/src/DryIoc/Container.cs b/src/DryIoc/Container.cs index 3df20b45..0348d47f 100644 --- a/src/DryIoc/Container.cs +++ b/src/DryIoc/Container.cs @@ -220,11 +220,14 @@ public IEnumerable ResolveManyCompileTimeGeneratedOrEmpty(Typ #region IRegistrator - /// Returns all registered service factories with their Type and optional Key. - /// Decorator and Wrapper types are not included. + /// public IEnumerable GetServiceRegistrations() => Registry.GetServiceRegistrations(_registry.Value); + /// + public IEnumerable GetDecoratorRegistrations() => + Registry.GetDecoratorRegistrations(_registry.Value); + // todo: @api Make `serviceKey` and `factoryType` optional /// Searches for registered factories by type, and key (if specified), /// and by factory type (by default uses ). @@ -2464,6 +2467,20 @@ public IEnumerable GetServiceRegistrations(Func } } + public static IEnumerable GetDecoratorRegistrations(ImHashMap registryOrServices) + { + if (registryOrServices is Registry r) + { + var ds = r.Decorators; + if (!ds.IsEmpty) + { + foreach (var entry in ds.Enumerate()) + foreach (var f in (Factory[])entry.Value) + yield return new DecoratorRegistrationInfo(f, entry.Key); + } + } + } + public static ImHashMap Register(ImHashMap registryOrServices, Factory factory, Type serviceType, IfAlreadyRegistered ifAlreadyRegistered, object serviceKey) { @@ -13982,7 +13999,32 @@ public enum IfAlreadyRegistered : byte AppendNewImplementation } - /// Existing registration info. + /// Decorator registration info. + + public struct DecoratorRegistrationInfo + { + /// Registered factory. + public Factory Factory; + + /// Decorator type. + public Type DecoratorType; + + /// Creates info. + public DecoratorRegistrationInfo(Factory factory, Type decoratorType) + { + Factory = factory; + DecoratorType = decoratorType; + } + + /// Pretty-prints info into the string. + public override string ToString() + { + var sb = new StringBuilder("DecoratorType=`").Print(DecoratorType).Append('`'); + return sb.Append(" with Factory=`").Append(Factory).Append('`').ToString(); + } + } + + /// Service registration info. public struct ServiceRegistrationInfo : IComparable { /// Registered factory. @@ -14027,13 +14069,13 @@ public ServiceRegistrationInfo(Factory factory, Type serviceType, object optiona /// Orders by registration public int CompareTo(ServiceRegistrationInfo other) => Factory.FactoryID - other.Factory.FactoryID; - /// Pretty-prints info to string. + /// Pretty-prints info into the string. public override string ToString() { - var s = new StringBuilder("ServiceType=`").Print(ServiceType).Append('`'); + var sb = new StringBuilder("ServiceType=`").Print(ServiceType).Append('`'); if (OptionalServiceKey != null) - s.Append(" with ServiceKey=`").Print(OptionalServiceKey).Append('`'); - return s.Append(" with Factory=`").Append(Factory).Append('`').ToString(); + sb.Append(" with ServiceKey=`").Print(OptionalServiceKey).Append('`'); + return sb.Append(" with Factory=`").Append(Factory).Append('`').ToString(); } } @@ -14071,6 +14113,9 @@ public interface IRegistrator /// Decorator and Wrapper types are not included. IEnumerable GetServiceRegistrations(); + /// Returns the curretnly registered decorators. There maybe multiple entries for a specific DecoratorRegistrationInfo.DecoratorTy`pe` + IEnumerable GetDecoratorRegistrations(); + /// Searches for registered factories by type, and key (if specified), /// and by factory type (by default uses ). /// May return empty, 1 or multiple factories. @@ -14316,31 +14361,55 @@ public string TryGetDetails(IContainer container) if (e == DryIoc.Error.WaitForScopedServiceIsCreatedTimeoutExpired) { var factoryId = (int)Details; + string decoratorMessage = null; // check `Request.CombineDecoratorWithDecoratedFactoryID()` for why is this logic - // var decoratorFactoryId = 0; // todo: @wip #598 add search for the `decoratorFactoryId` if (factoryId > ushort.MaxValue) { - // decoratorFactoryId = factoryId & ushort.MaxValue; - factoryId = factoryId >> 16; + var decoratorFactoryId = factoryId & ushort.MaxValue; + factoryId >>>= 16; + decoratorMessage = GetDecoratorMessage(container, decoratorFactoryId); + static string GetDecoratorMessage(IContainer container, int decoratorFactoryId) + { + foreach (var decoratorReg in container.GetDecoratorRegistrations()) + { + var f = decoratorReg.Factory; + if (f.FactoryID == decoratorFactoryId) + return $"Decorator registration related to the problem is:{NewLine}{decoratorReg}"; + var genFactories = f.GeneratedFactories; + if (genFactories != null) + foreach (var genEntry in f.GeneratedFactories.Enumerate()) + { + var generatedFactory = genEntry.Value; + if (generatedFactory.FactoryID == decoratorFactoryId) + return $"Decorator registration related to the problem is:{NewLine}{decoratorReg}{NewLine}Specifically, the generated closed-generic factory is:{NewLine}{generatedFactory}"; + } + } + return $"Unable to find the Decorator registration for the problematic factory with FactoryID={decoratorFactoryId}"; + } } - foreach (var reg in container.GetServiceRegistrations()) + var serviceMessage = GetServiceMessage(container, factoryId); + static string GetServiceMessage(IContainer container, int factoryId) { - var f = reg.Factory; - if (f.FactoryID == factoryId) - return $"The service registration related to the problem is:{NewLine}{reg}"; - var genFactories = f.GeneratedFactories; - if (genFactories != null) - foreach (var genEntry in f.GeneratedFactories.Enumerate()) - { - var generatedFactory = genEntry.Value; - if (generatedFactory.FactoryID == factoryId) - return $"The service registration related to the problem is:{NewLine}{reg}{NewLine}Specifically, its closed-generic factory is:{NewLine}{generatedFactory}"; - } + foreach (var serviceReg in container.GetServiceRegistrations()) + { + var f = serviceReg.Factory; + if (f.FactoryID == factoryId) + return $"Service registration related to the problem is:{NewLine}{serviceReg}"; + var genFactories = f.GeneratedFactories; + if (genFactories != null) + foreach (var genEntry in f.GeneratedFactories.Enumerate()) + { + var generatedFactory = genEntry.Value; + if (generatedFactory.FactoryID == factoryId) + return $"Service registration related to the problem is:{NewLine}{serviceReg}{NewLine}Specifically, the generated closed-generic factory is:{NewLine}{generatedFactory}"; + } + } + return $"Unable to find the Service registration for the problematic factory with FactoryID={factoryId}"; } - return "Unable to find the service registration for the problematic factory with FactoryID={factoryId}"; + return decoratorMessage == null ? serviceMessage : decoratorMessage + NewLine + serviceMessage; } return string.Empty; } diff --git a/test/DryIoc.IssuesTests/GHIssue391_Deadlock_during_Resolve.cs b/test/DryIoc.IssuesTests/GHIssue391_Deadlock_during_Resolve.cs index b2a3b859..ecfff754 100644 --- a/test/DryIoc.IssuesTests/GHIssue391_Deadlock_during_Resolve.cs +++ b/test/DryIoc.IssuesTests/GHIssue391_Deadlock_during_Resolve.cs @@ -7,9 +7,10 @@ public class GHIssue391_Deadlock_during_Resolve : ITest { public int Run() { + Test_non_generic_with_Decorator(); // actually related to the #598 Test_non_generic(); Test_open_generic(); - return 2; + return 3; } [Test] @@ -22,13 +23,13 @@ public void Test_non_generic() .WithDefaultIfAlreadyRegistered(IfAlreadyRegistered.Replace)); container.Register(typeof(A), Reuse.Singleton, setup: Setup.With(asResolutionCall: true)); - container.RegisterMany(new [] { typeof(A), typeof(IA) }, typeof(A), Reuse.Singleton, setup: Setup.With(asResolutionCall: true)); + container.RegisterMany(new[] { typeof(A), typeof(IA) }, typeof(A), Reuse.Singleton, setup: Setup.With(asResolutionCall: true)); container.Register(typeof(B), Reuse.Singleton, setup: Setup.With(asResolutionCall: true)); - container.RegisterMany(new [] { typeof(B), typeof(IB) }, typeof(B), Reuse.Singleton, setup: Setup.With(asResolutionCall: true)); + container.RegisterMany(new[] { typeof(B), typeof(IB) }, typeof(B), Reuse.Singleton, setup: Setup.With(asResolutionCall: true)); container.Register(typeof(C), Reuse.Singleton, setup: Setup.With(asResolutionCall: true)); - container.RegisterMany(new [] { typeof(C), typeof(IC) }, typeof(C), Reuse.Singleton, setup: Setup.With(asResolutionCall: true)); + container.RegisterMany(new[] { typeof(C), typeof(IC) }, typeof(C), Reuse.Singleton, setup: Setup.With(asResolutionCall: true)); // the missing dependency // container.Register(Reuse.Singleton); @@ -45,6 +46,43 @@ public void Test_non_generic() StringAssert.Contains("A`", m); } + [Test] + public void Test_non_generic_with_Decorator() + { + var container = new Container(rules => rules + .With(FactoryMethod.ConstructorWithResolvableArguments) + .WithoutEagerCachingSingletonForFasterAccess() + .WithoutThrowOnRegisteringDisposableTransient() + .WithDefaultIfAlreadyRegistered(IfAlreadyRegistered.Replace)); + + container.Register(Reuse.Singleton, setup: Setup.Decorator); + + container.Register(typeof(A), Reuse.Singleton, setup: Setup.With(asResolutionCall: true)); + container.RegisterMany(new[] { typeof(A), typeof(IA) }, typeof(A), Reuse.Singleton, setup: Setup.With(asResolutionCall: true)); + + container.Register(typeof(B), Reuse.Singleton, setup: Setup.With(asResolutionCall: true)); + container.RegisterMany(new[] { typeof(B), typeof(IB) }, typeof(B), Reuse.Singleton, setup: Setup.With(asResolutionCall: true)); + + container.Register(typeof(C), Reuse.Singleton, setup: Setup.With(asResolutionCall: true)); + container.RegisterMany(new[] { typeof(C), typeof(IC) }, typeof(C), Reuse.Singleton, setup: Setup.With(asResolutionCall: true)); + + // the missing dependency + // container.Register(Reuse.Singleton); + + // A -> B -> C -> D(missing) + // \-----> + + Assert.Throws(() => container.Resolve()); + + var ex = Assert.Throws(() => container.Resolve()); + Assert.AreSame(Error.NameOf(Error.WaitForScopedServiceIsCreatedTimeoutExpired), ex.ErrorName); + + var m = ex.TryGetDetails(container); + StringAssert.Contains("DecoratorType=", m); + StringAssert.Contains("ServiceType=", m); + StringAssert.Contains("IA`", m); + } + [Test] public void Test_open_generic() { @@ -55,13 +93,13 @@ public void Test_open_generic() .WithDefaultIfAlreadyRegistered(IfAlreadyRegistered.Replace)); container.Register(typeof(A<>), Reuse.Singleton, setup: Setup.With(asResolutionCall: true)); - container.RegisterMany(new [] { typeof(A<>), typeof(IA<>) }, typeof(A<>), Reuse.Singleton, setup: Setup.With(asResolutionCall: true)); + container.RegisterMany(new[] { typeof(A<>), typeof(IA<>) }, typeof(A<>), Reuse.Singleton, setup: Setup.With(asResolutionCall: true)); container.Register(typeof(B), Reuse.Singleton, setup: Setup.With(asResolutionCall: true)); - container.RegisterMany(new [] { typeof(B), typeof(IB) }, typeof(B), Reuse.Singleton, setup: Setup.With(asResolutionCall: true)); + container.RegisterMany(new[] { typeof(B), typeof(IB) }, typeof(B), Reuse.Singleton, setup: Setup.With(asResolutionCall: true)); container.Register(typeof(C), Reuse.Singleton, setup: Setup.With(asResolutionCall: true)); - container.RegisterMany(new [] { typeof(C), typeof(IC) }, typeof(C), Reuse.Singleton, setup: Setup.With(asResolutionCall: true)); + container.RegisterMany(new[] { typeof(C), typeof(IC) }, typeof(C), Reuse.Singleton, setup: Setup.With(asResolutionCall: true)); // the missing dependency // container.Register(Reuse.Singleton); @@ -78,27 +116,33 @@ public void Test_open_generic() StringAssert.Contains("A<>", m); } - public interface IA {} - public interface IA {} - public interface IB {} - public interface IC {} - public interface ID {} + public interface IA { } + public interface IA { } + public interface IB { } + public interface IC { } + public interface ID { } public class A : IA { - private IB B; - private IC C; - public A(IC c,IB b) + public IB B; + public IC C; + public A(IC c, IB b) { B = b; C = c; } } + public class AD : IA + { + public IA A; + public AD(IA a) => A = a; + } + public class A : IA { - private TB B; - private IC C; + public TB B; + public IC C; public A(IC c, TB b) { B = b; @@ -106,19 +150,18 @@ public A(IC c, TB b) } } - public class B : IB { - private IC C; + public IC C; public B(IC c) => C = c; } public class C : IC { - private ID D; + public ID D; public C(ID d) => D = d; } - public class D : ID {} + public class D : ID { } } } \ No newline at end of file diff --git a/test/DryIoc.TestRunner/Program.cs b/test/DryIoc.TestRunner/Program.cs index fd684cc0..19dee179 100644 --- a/test/DryIoc.TestRunner/Program.cs +++ b/test/DryIoc.TestRunner/Program.cs @@ -8,6 +8,8 @@ public class Program { public static void Main() { + // new GHIssue391_Deadlock_during_Resolve().Run(); + RunAllTests(); // new GHIssue588_Container_IsDisposed_property_not_reflecting_own_scope_disposed_state().Run(); @@ -18,7 +20,6 @@ public static void Main() // new RequiredPropertiesTests().Run(); // new PropertyResolutionTests().Run(); // new IssuesTests.Samples.DefaultReuseTest().Run(); - // new GHIssue391_Deadlock_during_Resolve().Run(); // new GHIssue559_Possible_inconsistent_behaviour().Run(); // new GHIssue557_WithFactorySelector_allows_to_Resolve_the_keyed_service_as_non_keyed().Run(); // new GHIssue565_Is_ignoring_ReuseScoped_setting_expected_behaviour_when_also_set_to_openResolutionScope().Run();