Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

registration error misses original reason #204

Closed
oleksabor opened this issue Nov 11, 2019 · 2 comments
Closed

registration error misses original reason #204

oleksabor opened this issue Nov 11, 2019 · 2 comments
Milestone

Comments

@oleksabor
Copy link

@oleksabor oleksabor commented Nov 11, 2019

Hi

Please help me how to get the error origin in case if Lamar (4.0.0) Container can not resolve the dependency:

[Subject("containerTest")]
public class nested_parameters
{
	static Container sut;
	Establish init = () => sut = new Container(_ =>
	{
		_.Scan(x => { x.TheCallingAssembly(); x.WithDefaultConventions(); });
	});

	Because of = () => {; };

	It some_not_null = () => sut.GetInstance<SomeImpl>().ShouldNotBeNull();
	It another_not_null = () => sut.GetInstance<AnotherImpl>().ShouldNotBeNull();

	It interface_resolved = () => sut.GetInstance<IInversion>().ShouldNotBeNull();
}
public class SomeImpl
{
	public SomeImpl(AnotherImpl impl) { }
}
public class AnotherImpl
{
	public AnotherImpl(IInversion pl) { }
}
public interface IInversion	{	}
public class Inversion2 : IInversion	{	}

interface_resolved fails because Inversion2 class name does not match to the interface name. The error message is ok and I understand what to do:

No service registrations exist or can be derived for IInversion

another_not_null fails with error message:

No service registrations exist or can be derived for AnotherImpl

This message does not contain the error origin and I do not know why it fails.

The same is for the some_not_null

No service registrations exist or can be derived for SomeImpl

Is there a chance to get the IInversion interface name in the error message for both some_not_null and another_not_null cases ?

The exception VS test output looks like:

Result StackTrace:	
Lamar.IoC.LamarMissingRegistrationException: No service registrations exist or can be derived for MessageClient.Test.AnotherImpl
   at Lamar.IoC.Scope.GetInstance(Type serviceType)
   at Lamar.IoC.Scope.GetInstance[T]()
   at MessageClient.Test.nested_parameters.<>c.<.ctor>b__6_3() in D:\MessageClient.Test\ContainerRegistryTest.cs:line 51
Result Message:	No service registrations exist or can be derived for MessageClient.Test.AnotherImpl

There is no inner exception to get missing interface registration info unfortunately

Regards.

@oleksabor

This comment has been minimized.

Copy link
Author

@oleksabor oleksabor commented Nov 12, 2019

I've found that public bool ServiceGraph.CouldBuild(Type concreteType) calls the public ConstructorInfo ConstructorInstance<T>.DetermineConstructor(ServiceGraph services, out string message)

and there is error properly formed.

Cannot fill the dependencies of any of the public constructors
Available constructors:new Bug_204_registration_error_misses_original_reason.AnotherImpl(Bug_204_registration_error_misses_original_reason.IInversion pl)
*Bug_204_registration_error_misses_original_reason.IInversion is not registered within this container and cannot be auto discovered by any missing family policy

However message variable value stays in the ServiceGraph.CouldBuild and never used any more.

Exception is raised in the public object Scope.GetInstance(Type serviceType) that is not aware about the original reason.

The simplest way is to throw the exception from the ConstructorInstance<T>.DetermineConstructor
However I'm afraid that this can break legacy logic that handles instance creation errors with if ( != null) checks.

Does it have sense to add new ServiceGraph property that will hold the ConstructorInstance<T>.DetermineConstructor error?
Then new property can be used when Scope.GetInstance raises exception, like

public class ServiceGraph : IDisposable
{
public string CouldBuildError {get; protected set;}
public bool CouldBuild(Type concreteType)
{
    var constructorInstance = new ConstructorInstance(concreteType, concreteType, ServiceLifetime.Transient);
    foreach (var policy in InstancePolicies)
    {
        policy.Apply(constructorInstance);
    }
    
    var ctor = constructorInstance.DetermineConstructor(this, out string message);
    
    CouldBuildErrror = message;
    return ctor != null && message.IsEmpty();
}
}
public class Scope : IServiceContext
// ENDSAMPLE
{
public object GetInstance(Type serviceType)
{
    assertNotDisposed();
    var resolver = ServiceGraph.FindResolver(serviceType);

    if (resolver == null)
    {
        throw new LamarMissingRegistrationException(ServiceGraph.CouldBuildError, serviceType);
    }

    return resolver(this);
}
}

The only one problem I see is multi threading issue if ServiceGraph instance is shared between several Scope (public Scope(ServiceGraph serviceGraph, Scope root))

@jeremydmiller jeremydmiller added this to the 4.1 milestone Jan 2, 2020
@jeremydmiller

This comment has been minimized.

Copy link
Contributor

@jeremydmiller jeremydmiller commented Jan 3, 2020

@oleksabor Good idea, and done. Just a wee bit different than what you suggested. This will be in Lamar 4.1 by early next week.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants
You can’t perform that action at this time.