This project is read-only.

Setup of the container

Aug 10, 2013 at 2:04 AM
Hi,

Ok, this question is most likely going to be very simple, and I'm very likely to have been extremely dumb, but I'm clearly missing something.

My background is Unity, and I'm used to the IModule construct, via a Bootstrapper and DirectoryModuleCatalogs, which essentially "sniff" assemblies for the types implementing IModule so that types can be registered with the container.

I have already seen the information at https://simpleinjector.codeplex.com/wikipage?title=Advanced-scenarios&referringTitle=Documentation#Plugins and rolled a more generic wrapper for this for re-use purposes. I've taken it further in order to:
  1. fix a bug whereby a .dll file might not be an Assembly (i.e a BadImageFormatException from Assembly.LoadFrom(...))
  2. create a type that implements, say IModule, so it can be called to register its own types for singleton or not purposes
My problem is that, being from a Unity background, I'm used to being able to call Resolve<T> on a type not actually registered with the container, but in order to rely on constructor injection. This is pretty vital for me as I essentially register singleton services at a high-level (app/window etc) such as ILogService, that I would like to have injected into a type registered by a "drop-in" module. However, if I do, inside a one of the IModule registrars, I run into the cannot change the container exception since GetInstance has been called (I called it to create a type with injection on the constructor just earlier).

I guess I may not be explaining myself too well (though I really hope I am), but this seems something obvious for an IoC to support, but the notion of container lockdown after the first resolution seems to completely preclude the notion of an assembly being dropped in completely outside my knowledge after delivery. Also, if a 3rd party module cannot do GetInstance so that base services are injected, this means the developer would need to pass a concrete instance, which might be a different implementation to the one used by the rest of the application. An odd situation surely?

Do I have to register all types as types (not instances) with creator jobbies so that when you go for one of those types through GetInstance<T>, the jobbie is called which can also use GetInstance<T> for the more base types?

Essentially, do I have to register things in a much more rigid way than Unity imposes or am I missing something more subtle?

In hoping someone can follow this drivel ( :D ), I give thanks for any replies in advance.

Simon
Aug 10, 2013 at 10:22 AM
Simple Injector's equivalent to IModule is an IPackage which is included in the SimpleInjector.Packaging NuGet package.

Packages can be included by calling:
container.RegisterPackages();
I'm not familiar with the DirectoryModuleCatalogs, but I expect it to read assemblies from a specific directory. You can achieve this as follows:
string pluginDirectory =
    Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins");

var pluginAssemblies =
    from file in new DirectoryInfo(pluginDirectory).GetFiles()
    where file.Extension == ".dll"
    select Assembly.LoadFile(file.FullName);

container.RegisterPackages(pluginAssemblies);
This allows you to dynamically load new plugins that are added 'after delivery', but this still forces you to do the loading of the plugin assemblies at application startup. This is usually a very reasonable thing to do. Being able to pick up new plugins while the application is running is simply a level of flexibility that most applications never need. ASP.NET and IIS are even built around this. If you place a new dll in the web app's bin directory, IIS will recycle your app domain. The web application is restarted.

From a dependency injection perspective, you generally want all dependencies available when the application is starting up. This allows you to verify and build complete object graphs. For this reason, both Simple Injector (and the Autofac container does this too btw) prevents any new registrations after the registration process is complete.

Besides, I would even argue that to prevent rigidity, you should really prevent doing any registrations after the application's startup phase. If you need to load plugins during mid-life, they should probably use their own container to register stuff into (if they even use a container).

But still, while Simple Injector locks down the container, there are still simple ways around this. I can't however give a single solution to this, because it depends on what your plugin architecture looks like. If you can go into a bit more depth about your particular plugin architecture, I can explain what the best way to do this with Simple Injector is. If it is impossible for you to share any of this online, please contact me through here. We can take it from there.
Marked as answer by dot_NET_Junkie on 2/26/2014 at 1:42 PM
Aug 10, 2013 at 11:51 AM
Morning,
I think I am actually getting my bonce around the idea that all registrations should be done before the first type creation from the container.
When using Unity, I would normally registers single instance services at application level, such as ILoggingService, INotificationService; stuff that provided basic application infrastructure.
Then, during modular registration, that module could (and I don’t know for sure because I didn’t write it) register its only single instance services. They would normally create their instance using container.Resolve<MyConcreteType>(), which would inject any constructors parameters such as INotificationService.
Now I believe under Simple Injector, that module provider would do container.GetInstance<MyConcreteType>() to achieve the injection. However, for the next module that registered basic types, they will run into the error that you cannot register after a call to GetInstance etc.
To try and provide a step by step of what is happening in my app (WPF) :
  1. App starts, I register core SingleInstance services
  2. I make the call so all packages (some I wrote, some I didn’t, they’re in the bin directory as you mention) can register their types
  3. Now in step 2 above, someone registers a SingleInstance, which needs one of those types registered in 1, so they do a GetInstance for injection
  4. Now in step 2 above, another module registers a type
  5. The get the error because another module vendor did GetInstance for injection purposes
It is probable that guidance needs to be given in the docs that when registering types in module initialisation, you only register types and not instances that require resolving injection?
As an aside, in the code sample you give in your reply, the Assembly.LoadFile can break with an exception if the .dll isn’t a .NET image I think. I have worked around that with an extra step in my own code. 😊
Thanks for replying, I appreciate it a lot. I think we are having a conceptual discussion at this point as I believe I can register all types without any physical instances in pass 1, then when binding kicks in, instances (singleton or per call) can be resolved with injection. This is different to Unity (which you clearly intended and I accept that) and I’m just trying to get my head around this difference so I can move forward with your container forever more in the future. 😊
Simon
Sent from Windows Mail
From: dot_NET_Junkie
Sent: ‎Saturday‎, ‎10‎ ‎August‎ ‎2013 ‎10‎:‎23
To: [email removed]

From: dot_NET_Junkie

Simple Injector's equivalent to IModule is an IPackage which is included in the SimpleInjector.Packaging NuGet package.

Packages can be included by calling:
container.RegisterPackages();
I'm not familiar with the DirectoryModuleCatalogs, but I expect it to read assemblies from a specific directory. You can achieve this as follows:
string pluginDirectory =
    Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins");

var pluginAssemblies =
    from file in new DirectoryInfo(pluginDirectory).GetFiles()
    where file.Extension == ".dll"
    select Assembly.LoadFile(file.FullName);

container.RegisterPackages(pluginAssemblies);
This allows you to dynamically load new plugins that are added 'after delivery', but this still forces you to do the loading of the plugin assemblies at application startup. This is usually a very reasonable thing to do. Being able to pick up new plugins while the application is running is simply a level of flexibility that most applications never need. ASP.NET and IIS are even built around this. If you place a new dll in the web app's bin directory, IIS will recycle your app domain. The web application is restarted.

From a dependency injection perspective, you generally want all dependencies available when the application is starting up. This allows you to verify and build complete object graphs. For this reason, both Simple Injector (and the Autofac container does this too btw) prevents any new registrations after the registration process is complete.

Besides, I would even argue that to prevent rigidity, you should really prevent doing any registrations after the application's startup phase. If you need to load plugins during mid-life, they should probably use their own container to register stuff into (if they even use a container).

But still, while Simple Injector locks down the container, there are still simple ways around this. I can't however give a single solution to this, because it depends on what your plugin architecture looks like. If you can go into a bit more depth about your particular plugin architecture, I can explain what the best way to do this with Simple Injector is. If it is impossible for you to share any of this online, please contact me through here. We can take it from there.
Aug 10, 2013 at 12:14 PM
Hi Simon,
someone registers a SingleInstance, which needs one of those types registered in 1, so they do a GetInstance for injection
There should not be a need to call GetInstance at that point in time. If that SingleInstance has constructor dependencies, you can simply register it as follows:
container.RegisterSingle<ISingleInstance, SingleInstance>();
// or when you register the concrete type
container.RegisterSingle<SingleInstance>();
The container will find out all dependencies and will resolve them at a later point in time.

If that doesn't work, for instance because that type has a special constructor that can't be auto-wired by the container, you might need to call the constructor manually, but there is still no need to call GetInstance at that point in time. You should register a Func<T> delegate that defers the creation of that type, as follows:
container.RegisterSingle<ISingleInstance>(() => 
    new SingleInstance("config value", container.GetInstance<IDependency>()));
I hope this solves your problem. If not, please let me know.
the Assembly.LoadFile can break with an exception if the .dll
That example was never meant to be a complete solution that works in all cases. It's just meant to show how to do things. But to be honest, if you have a plugin directory for your application, why do you want place dlls in there that can't be handled by the application. I would say that this would be an error and it is right to throw an exception (instead of silently skipping over that dll).


ps. Would you be so kind to update your last response and remove all text from "Sent from Windows Mail" and below. That keeps the discussion clean, and makes it easier for others to read in the future.
Aug 10, 2013 at 12:47 PM
Hi,

Thanks for the speedy reply.

Ok, I'm starting to get and like this. I've done what you say and rather than do

container.RegisterSingle<IInterface>(container.GetInstance<InterfaceImpl>()) as I would've in Unity, I've done the following

container.RegisterSingle<IInterface,InterfaceImpl>()

And then, when all types are done, call container.Verify() which causes all types registered like the above to get created. That is actually pretty neat. :)

Can I assume that this gets around any module dependency problems? In Unity, you had to decorate IModule classes with [ModuleDependency("modulename")] in order to hint at a load and thus registration order.

As for a .dll that isn't an assembly; that happens when someone writes an extension that has none .NET dependencies. I admit, we're still wondering a bit on this one and think those non .NET dlls should be in the main bin folder, but we often configure the modules to be found from there, so this problem could still arise.

I accept those code snippets were only examples and not production ready, I thought it might have been worth pointing out in case someone else was reading. :)

I think I'm almost there on this.

Simon


Aug 10, 2013 at 1:27 PM
Can I assume that this gets around any module dependency problems? In Unity, you had to decorate IModule classes with [ModuleDependency("modulename")] in order to hint at a load and thus registration order.
I'm not sure what 'module dependency problems' you might have. The order in which things are registered however, should in general not matter.
Aug 10, 2013 at 2:16 PM
Hi,

Sorry for the confusion, my question was hypothetical. In Unity, you can hit dependency problems so have to use the attribute to influence the load order.

I think in Simple Injector, because types are registered, even for SingleInstance and then created on Verify, all types will be registered by all modules at that point meaning module dependency issues should be next to impossible. :)

I actually like the 2 pass register/create way Simple Injector works. It results in a much more robust container, which feels way cleaner.

Simon