I am having trouble retrieving an instance using lightinject and .net core. Originally this was a comprehensive set of classes etc. but I've simplified it.
In my startup class, ConfigureServices method I have the following code;
var container = new ServiceContainer();
container.Register<IHasEnum, HasEnum>();
var test = container.GetInstance<IHasEnum>();
The third line generates an exception;
System.MissingMethodException
No parameterless constructor defined for this object.
at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, bool publicOnly, bool noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck)
at System.RuntimeType.CreateInstanceSlow(bool publicOnly, bool skipCheckThis, bool fillCache, StackCrawlMark& stackMark)
at System.Activator.CreateInstance(Type type, bool nonPublic)
at System.Activator.CreateInstance(Type type)
at LightInject.<>c.<.ctor>b__20_0(Type type) in C:\projects\lightinject\build\tmp\netstandard13\Binary\LightInject\LightInject.cs:line 1695
at LightInject.CompositionRootExecutor.Execute(Type compositionRootType) in C:\projects\lightinject\build\tmp\netstandard13\Binary\LightInject\LightInject.cs:line 6165
at LightInject.AssemblyScanner.ExecuteCompositionRoots(IEnumerable<Type> compositionRoots) in C:\projects\lightinject\build\tmp\netstandard13\Binary\LightInject\LightInject.cs:line 6261
at LightInject.ServiceContainer.GetEmitMethod(Type serviceType, string serviceName) in C:\projects\lightinject\build\tmp\netstandard13\Binary\LightInject\LightInject.cs:line 3077
at LightInject.ServiceContainer.GetEmitMethodForDependency(Dependency dependency) in C:\projects\lightinject\build\tmp\netstandard13\Binary\LightInject\LightInject.cs:line 3453
at LightInject.ServiceContainer.EmitPropertyDependency(IEmitter emitter, PropertyDependency propertyDependency, LocalBuilder instanceVariable) in C:\projects\lightinject\build\tmp\netstandard13\Binary\LightInject\LightInject.cs:line 3432
at LightInject.ServiceContainer.EmitPropertyDependencies(ConstructionInfo constructionInfo, IEmitter emitter) in C:\projects\lightinject\build\tmp\netstandard13\Binary\LightInject\LightInject.cs:line 3515
at LightInject.ServiceContainer.EmitNewInstanceUsingImplementingType(IEmitter emitter, ConstructionInfo constructionInfo, Action<IEmitter> decoratorTargetEmitMethod) in C:\projects\lightinject\build\tmp\netstandard13\Binary\LightInject\LightInject.cs:line 3346
at LightInject.ServiceContainer.EmitNewInstance(ServiceRegistration serviceRegistration, IEmitter emitter) in C:\projects\lightinject\build\tmp\netstandard13\Binary\LightInject\LightInject.cs:line 3301
at LightInject.<>c__DisplayClass147_0.<CreateEmitMethodWrapper>b__0(IEmitter ms) in C:\projects\lightinject\build\tmp\netstandard13\Binary\LightInject\LightInject.cs:line 3122
at LightInject.ServiceContainer.CreateDynamicMethodDelegate(Action<IEmitter> serviceEmitter) in C:\projects\lightinject\build\tmp\netstandard13\Binary\LightInject\LightInject.cs:line 3044
at LightInject.ServiceContainer.CreateDelegate(Type serviceType, string serviceName, bool throwError) in C:\projects\lightinject\build\tmp\netstandard13\Binary\LightInject\LightInject.cs:line 3909
at LightInject.ServiceContainer.CreateDefaultDelegate(Type serviceType, bool throwError) in C:\projects\lightinject\build\tmp\netstandard13\Binary\LightInject\LightInject.cs:line 3872
at LightInject.ServiceContainer.GetInstance(Type serviceType) in C:\projects\lightinject\build\tmp\netstandard13\Binary\LightInject\LightInject.cs:line 2541
at LightInject.ServiceContainer.GetInstance<TService>() in C:\projects\lightinject\build\tmp\netstandard13\Binary\LightInject\LightInject.cs:line 2591
The class in question, HasEnum is here;
public class HasEnum : IHasEnum
{
private Colours _colours;
public Colours PickedColour
{
get
{
return _colours;
}
internal set { _colours = Colours.Red; }
}
}
As per the recommendation of the exception, I added a default constructor to HasEnum and it is the same result. What is quite maddening is when I try this in a seperate (heavily simplified) project it seems to work ok. I've checked the version of lightinject in both projects and they match. Have I set some kind of setting somewhere unknowingly?
Many thanks
EDIT - Curiously, if I remove the setter from the PickedColour property this error does not occur.
public Colours PickedColour
{
get { return _colours; }
}
So it seems to be something to do with the setter on this enum property. Even if I put a blank set{} this exception throws.
edit 2 : the IHasEnum interface;
public interface IHasEnum
{
Colours PickedColour { get; }
}
edit 3: Thank you for your responses. In terms of using lightinject within .net core, there isn't a huge amount of documentation. The .net core specific version tells you to use it like this : http://www.lightinject.net/microsoft.dependencyinjection/
I have followed this, the error with my test class persists. If I simplify the test class by removing the enum type it works fine.
To further clarify exactly how I'm registering the services, here are the relevant snippets across multiple files and classes;
// The startup method creates the config
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables();
builder.AddInMemoryCollection();
Configuration = builder.Build();
}
// The configure method passes The configuration to a new
// application object - this is what creates the lightinject container
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddSingleton(Configuration);
var app = new PocApplication(Configuration); // the main section of PocApplication is below this method
var service = services.AddPocCore(app); // This extension method is listed below, is in another file.
return service;
}
// This method creates the ServiceContainer
public PocApplication(IConfiguration config)
{
var container = new ServiceContainer();
Container = container;
// Other items are omitted, the ServiceContainer
// is passed back.
}
public static IServiceProvider AddPocCore(this IServiceCollection services, PocApplication pocApplication)
{
var pocContainer = pocApplication.Container;
pocContainer.Register<IHasEnum, HasEnum>();
// other services registered here
return services.WrapAspNetContainer(pocApplication.Container); // another extension, shown below.
}
public static IServiceProvider WrapAspNetContainer(this IServiceCollection services, IServiceContainer container)
{
return container.CreateServiceProvider(services);
// This is the way to use lightinject to replace the container with the custom
// one as per the documentation.
}
I've removed everything apart from the relevant parts. When execution reaches the home controller and I try to get the IHasEnum implementation, the same exception results. I have used the registration as per the documentation, not sure how I can get this to work.
Many thanks.
edit 4:
I Have narrowed down source of the error - but can't understand why it's occurring. The implementation of IHasEnum has been a red herring - it is being registered correctly. I found a class which implements ICompsitionRoot - once I removed this usage the HasEnum instance is retrieved. According to the lightInject docs ( http://www.lightinject.net/#composition-root ) any class that implements ICompositionRoot will be executed when the DI is set up. The composition class implementing this interface is necessary to construct a settings object from the appsettings.json file.
To demonstrate this, I'll show the AddPocCore method again, with the added line with a call to the extension method which composes the Configuration composition Root;
public static IServiceProvider AddPocCore(this IServiceCollection services, PocApplication pocApplication)
{
var pocContainer = pocApplication.Container;
pocContainer.Register<IHasEnum, HasEnum>();
// The registerfrom extension method - shown below.
container.RegisterFrom(new ConfigurationCompositionRoot(application.Configuration));
return services.WrapAspNetContainer(pocApplication.Container); // another extension, shown below.
}
public static void RegisterFrom<TCompositionRoot>(this IServiceRegistry container, TCompositionRoot instance)
where TCompositionRoot : ICompositionRoot
{
// this is where the compose method is called.
instance.Compose(container);
}
// Either removing ICompositionRoot inheritance or the constructor means the error doesn't occur
public sealed class ConfigurationCompositionRoot : ICompositionRoot
{
private readonly IConfiguration _config;
public ConfigurationCompositionRoot(IConfiguration config)
{
// I think this is where the default constructor exception method is
// being generated, debugging shows the config object is instantiated here.
_config = config;
}
public void Compose(IServiceRegistry container)
{
// This is where the config is used to create the settings objects
// which includes the database connection string.
}
}
So essentially, as soon as I remove the call to the extension method, then within ConfigurationCompositionRoot I remove the ICompositionRoot from the class declaration (stopping lightinject from intercepting it) the program works as expected. I just can't understand how I can use this composition root to get my settings into an object on startup. This whole thing is getting rather long, sorry about this - just really want to get this working with dot net core.
Thanks again.