Skip to content

Commit

Permalink
fixed: #598 - added the info about the problematic decorator + test;
Browse files Browse the repository at this point in the history
new exposed method IRegistrator.GetDecoratorRegistrations();
  • Loading branch information
dadhi committed Oct 17, 2023
1 parent 0f28f26 commit 7b7f533
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 45 deletions.
115 changes: 92 additions & 23 deletions src/DryIoc/Container.cs
Original file line number Diff line number Diff line change
Expand Up @@ -220,11 +220,14 @@ public IEnumerable<ResolveManyResult> ResolveManyCompileTimeGeneratedOrEmpty(Typ

#region IRegistrator

/// <summary>Returns all registered service factories with their Type and optional Key.</summary>
/// <remarks>Decorator and Wrapper types are not included.</remarks>
/// <inheritdoc />
public IEnumerable<ServiceRegistrationInfo> GetServiceRegistrations() =>
Registry.GetServiceRegistrations(_registry.Value);

/// <inheritdoc />
public IEnumerable<DecoratorRegistrationInfo> GetDecoratorRegistrations() =>
Registry.GetDecoratorRegistrations(_registry.Value);

// todo: @api Make `serviceKey` and `factoryType` optional
/// <summary>Searches for registered factories by type, and key (if specified),
/// and by factory type (by default uses <see cref="FactoryType.Service"/>).
Expand Down Expand Up @@ -2464,6 +2467,20 @@ public IEnumerable<R> GetServiceRegistrations<R>(Func<Type, object, Factory, R>
}
}

public static IEnumerable<DecoratorRegistrationInfo> GetDecoratorRegistrations(ImHashMap<Type, object> 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<Type, object> Register(ImHashMap<Type, object> registryOrServices,
Factory factory, Type serviceType, IfAlreadyRegistered ifAlreadyRegistered, object serviceKey)
{
Expand Down Expand Up @@ -13982,7 +13999,32 @@ public enum IfAlreadyRegistered : byte
AppendNewImplementation
}

/// <summary>Existing registration info.</summary>
/// <summary>Decorator registration info.</summary>

public struct DecoratorRegistrationInfo
{
/// <summary>Registered factory.</summary>
public Factory Factory;

/// <summary>Decorator type.</summary>
public Type DecoratorType;

/// <summary>Creates info.</summary>
public DecoratorRegistrationInfo(Factory factory, Type decoratorType)
{
Factory = factory;
DecoratorType = decoratorType;
}

/// <summary>Pretty-prints info into the string.</summary>
public override string ToString()
{
var sb = new StringBuilder("DecoratorType=`").Print(DecoratorType).Append('`');
return sb.Append(" with Factory=`").Append(Factory).Append('`').ToString();
}
}

/// <summary>Service registration info.</summary>
public struct ServiceRegistrationInfo : IComparable<ServiceRegistrationInfo>
{
/// <summary>Registered factory.</summary>
Expand Down Expand Up @@ -14027,13 +14069,13 @@ public ServiceRegistrationInfo(Factory factory, Type serviceType, object optiona
/// <summary>Orders by registration</summary>
public int CompareTo(ServiceRegistrationInfo other) => Factory.FactoryID - other.Factory.FactoryID;

/// <summary>Pretty-prints info to string.</summary>
/// <summary>Pretty-prints info into the string.</summary>
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();
}
}

Expand Down Expand Up @@ -14071,6 +14113,9 @@ public interface IRegistrator
/// Decorator and Wrapper types are not included.</summary>
IEnumerable<ServiceRegistrationInfo> GetServiceRegistrations();

/// <summary>Returns the curretnly registered decorators. There maybe multiple entries for a specific DecoratorRegistrationInfo.DecoratorTy`pe`</summary>
IEnumerable<DecoratorRegistrationInfo> GetDecoratorRegistrations();

/// <summary>Searches for registered factories by type, and key (if specified),
/// and by factory type (by default uses <see cref="FactoryType.Service"/>).
/// May return empty, 1 or multiple factories.</summary>
Expand Down Expand Up @@ -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;
}
Expand Down
85 changes: 64 additions & 21 deletions test/DryIoc.IssuesTests/GHIssue391_Deadlock_during_Resolve.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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<ID, D>(Reuse.Singleton);
Expand All @@ -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<IA, AD>(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<ID, D>(Reuse.Singleton);

// A -> B -> C -> D(missing)
// \----->

Assert.Throws<ContainerException>(() => container.Resolve<IA>());

var ex = Assert.Throws<ContainerException>(() => container.Resolve<IA>());
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()
{
Expand All @@ -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<ID, D>(Reuse.Singleton);
Expand All @@ -78,47 +116,52 @@ public void Test_open_generic()
StringAssert.Contains("A<>", m);
}

public interface IA {}
public interface IA<TB> {}
public interface IB {}
public interface IC {}
public interface ID {}
public interface IA { }
public interface IA<TB> { }
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<TB> : IA<TB>
{
private TB B;
private IC C;
public TB B;
public IC C;
public A(IC c, TB b)
{
B = b;
C = c;
}
}


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 { }
}
}
3 changes: 2 additions & 1 deletion test/DryIoc.TestRunner/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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();
Expand Down

0 comments on commit 7b7f533

Please sign in to comment.