Injecting all instances of a given type
I’m sure I’m a little late to the game here, but I recently stumbled across how to do collection injection with Structure Map. I’ve known it was possible, and have seen it hinted at in other blogs posts for a while, but I just didn’t know how exactly it was done.
Why would you want to do this? Lets take an actual situation from our system. We rolled a small scheduled task framework (sorry Quartz.NET and Topshelf, you just didn’t fit the bill for our needs). I wanted us to just drop a POCO object in the project and implement an ITask
interface, which had exactly one method: Run()
. I also wanted these objects to be pulled from the container so they could have services/repositories injected in as needed.
To make it easy for our network admins to schedule these tasks, I wrote the application to take a single **/run
Simple enough, right?
My first stab at it, and it actually ran for a while like this, took in Structure Map’s IContainer
to retrieve the task with all its dependencies satisfied, and to display all the available tasks it would just loop through all types in the assembly (filtered for only those implementing ITask
), and get the needed display information.
Worked fine and dandy, but testing it was slow. I had to scan all the types in the assembly, even if I knew I was looking for a single test dummy class. I didn’t like it, so I began tinkering with Structure Map. I wanted the fully initialized objects (from the container) injected in as an IEnumerable<ITask>
, so I could just pluck the needed one to run, and loop through that list to build up the small display when needed. It would be easier to read and also make testing quicker and simpler. Here’s what I ended up with:
public static void BootstrapStructureMap()
{
ObjectFactory.Initialize(y =>
{
y.Scan(x =>
{
x.TheCallingAssembly();
x.WithDefaultConventions();
x.AddAllTypesOf();
});
y.ForRequestedType<>()
.TheDefault.Is.ConstructedBy(x => ObjectFactory.GetAllInstances());
});
}
This is called while our app is boot strapping (though we use separate Registries for each area of the app, I just simplified it for posting). Pretty self explanatory - the AddAllTypesOf
tells Structure Map to gather up all implementors of ITask
in the assembly, and we then tell Structure Map to get all instances of that interface to pass it in whenever IEnumerable<ITask>
is requested. Without that, you’d have to take in an array of ITask's
. Same difference, I just prefer enumerables.
So there you go - get a collection of fully constructed types injected into your objects. We did this assembly type looping in one or two other system startup areas of the app, and I was able to kill them all off with this technique while shaving a good 4 seconds off our unit test suite. Pretty neato.
As a moral, of sorts, for this story, I also learned after doing all this to always RTFM when you have a question. It’ll save you a lot of time tinkering on your own :)