Loading a Type specified in web.config, for example a Ninject Module
Yesterdays article about having your own configuration section in web.config included the option injectionModule:
<myApp injectModule="MyApp.MyAppTestNinjectModule">
The reason I am doing that is because I use Ninject 2 to do dependency injection on my ASP.net MVC 2 app.. Basically I have multiple database backends for my application and the TestNinjectModule implements an in-memory List so that I can develop the application without caring about data persistence yet.
Ninject uses so called Modules that specify the bindings. I have two modules, and my Test Module looks like this:
public class MyAppTestNinjectModule : NinjectModule
{
public override void Load()
{
// As the Test Repositories usually use internal Lists, they need to be Singleton
Bind<IProjectRepository>().To<TestProjectRepository>().InSingletonScope();
Bind<INoteRepository>().To<TestNoteRepository>().InSingletonScope();
}
}
So everytime Ninject sees IProjectRepository, it knows that it should give me the class TestProjectRepository. (Bonus Tip: When using List<T> as data storage, use InSingletonScope to make sure one instance is shared throughout the entire Application) My second Ninject module looks identical, except that I use MSSqlProjectRepository.
I wanted to have this configurable so that I can easily change between them or add more. Previously, creation of the Kernel (CreateKernel in global.asax) looked like this:
protected override IKernel CreateKernel()
{
return new StandardKernel(new MyAppTestNinjectModule());
}
See that call to new MyAppTestNinjectModule()? If I want to change it, I need to recompile and redeploy the App.
The change itself is straight-forward if you know a bit of reflection. Here is what we need to do:
- Get the name of the Class as a string
- Find the Type that has this name
- Instantiate it
- Pass the instance to the StandardKernel constructor
Here is the overly commented code to do that:
protected override IKernel CreateKernel()
{
// MyAppSettings is the class that reads the setting from
// web.config
string moduleName = MyAppSettings.InjectModule;
// Type.GetType takes a string and tries to find a Type with
// the *fully qualified name* - which includes the Namespace
// and possibly also the Assembly if it's in another assembly
Type moduleType = Type.GetType(moduleName);
// If Type.GetType can't find the type, it returns Null
NinjectModule module;
if (moduleType != null)
{
// Activator.CreateInstance calls the parameterless constructor
// of the given Type to create an instace. As this returns object
// you need to cast it to the desired type, NinjectModule
module = Activator.CreateInstance(moduleType) as NinjectModule;
}
else
{
// If the Type was not found, you need to handle that. You could instead
// initialize Module through some default type, for example
// module = new MyAppDefaultNinjectModule();
// or error out - whatever suits your needs
throw new MyAppConfigException(
string.Format("Could not find Type: '{0}'", moduleName),
"injectModule");
}
// As module is an instance of a NinjectModule (or derived) class, we
// can use it to create Ninject's StandardKernel
return new StandardKernel(module);
}