If you’ve been following me lately on Twitter or in this blog, you know I’ve been busy working on a new project called BlueMilk that’s meant to be a much faster replacement for the venerable StructureMap library. There are some advanced StructureMap features that will not be included into BlueMilk. Otherwise though, I’m aiming to make BlueMilk a near drop in replacement for most StructureMap users in terms of behavior and API. That being said, there are some important differences in behavior that you’ll need to be aware of switching from StructureMap to BlueMilk. In all the cases discussed below, the change was to ensure better compatibility with how our ASP.Net Core overlords believe IoC tools work. Bitterness at the ASP.net team aside, some of these design changes led to almost dramatically simpler internals and performance in BlueMilk compared to StructureMap, so it’s still a win overall.
I dropped some of the old StructureMap verbiage just to be more consistent with ASP.Net Core’s verbiage.
- “PluginType” becomes “ServiceType”
- “PluggedType” becomes “ImplementationType”
- “Instance” is still the name of the BlueMilk unit of registration (it’s basically a superset of ServiceDescriptor in ASP.Net Core), but the API signatures change
- “Container” is still the same
While this has been pluggable and configurable forever, by default StructureMap tries to use the “greediest” public constructor it can find on a concrete type. If there are multiple constructors with the same number of arguments, it uses the first one it encounters.
In BlueMilk, we follow the ASP.Net Core prescribed logic of choosing the greediest constructor where BlueMilk has some known registration for each argument. I’ve always hated this idea, purposely kept it out of StructureMap, and I feel like it’s a bad pattern to use with IoC tools because of the extra mental overhead in understanding what’s going on (it’s an addon for ASP.Net Core usage in StructureMap 4.*), but that’s not worth fighting over.
I don’t like this behavior because it can wallpaper over problems with registrations and give you some false positives. My advice when using an IoC container is to build your classes as if they’re always built by the IoC container. In my experience, folks get in trouble when they try to be half in and half out of IoC usage with multiple constructors and optional arguments.
Object Lifetime / Lifecycle
This one’s a big difference, so do watch out. BlueMilk changes to using the ASP.Net Core verbiage and logic for service lifecycles. Singleton and Scoped work identically in BlueMilk as they did in StructureMap, except that BlueMilk is a lot better about tracking
IDisposable dependencies within singleton or scoped construction.
The new “Transient” really maps to StructureMap’s “UniquePerRequest” lifecycle. If you depended on StructureMap’s default “Transient” behavior from before to have objects scoped per logical request or within a nested container, you’ll probably want to switch to the “Scoped” lifecycle.
Use vs. Add No Longer Matters
In the very beginning — and remember that StructureMap is literally the first .Net IoC container so there was no prior art — I had the belief that in the case of multiple registrations for the same service type, the user should explicitly tell StructureMap which one is the default that gets resolved from a call to
IContainer.GetInstance(type) and which registrations are just additional services that would be either resolved by name or by an enumerable of all of them.
BlueMilk just uses the ASP.Net Core compliant logic of saying that the very last registration against a service type is the default registration.
In more concrete terms, the old StructureMap
Add() methods now mean the exact same thing, and the last one wins. I might mark one or the other as
[Obsolete] just to make the transitions go a little easier. See this code:
var container = new Container(_ =>
// In StructureMap, this would be the default
// In BlueMilk, this would be because it's last