Neat Tricks with StructureMap
I’ve been getting a crash course in StructureMap the past 2 weeks. It’s been going pretty well especially when you consider that the author/maintainer sits next to me every day 🙂
We’ve been using the new 2.5-ish trunk code and there are some cool features I’d like to share with you and maybe get you pumped about the upcoming 2.5 release.
Auto-registration of obvious types
This one is particularly cool since most of the types registered in your container are ISomeService with an associated SomeService implementation.
Simply put: If you have a class Foo that implements interface IFoo, StructureMap, if asked to do so, will auto-register Foo as the default concrete type for plugin family IFoo. No config necessary! Phew… just saved the world some serious angle brackets tax there. Every bit counts, trust me — I came from a SpringFramework.NET background which is rich in its XML configuration.
Convention-based Discovery/Registration
When calling StructureMapConfiguration.ScanAssemblies(), you can now provide your own type registration conventions in addition to the default one. For example:
1: StructureMapConfiguration.ScanAssemblies()
<pre style="padding-right: 0px;padding-left: 0px;font-size: 8pt;padding-bottom: 0px;margin: 0em;overflow: visible;width: 100%;color: black;border-top-style: none;line-height: 12pt;padding-top: 0px;font-family: consolas, 'Courier New', courier, monospace;border-right-style: none;border-left-style: none;background-color: #f4f4f4;border-bottom-style: none"><span style="color: #606060"> 2:</span> .With<DefaultConventionScanner>()</pre>
<pre style="padding-right: 0px;padding-left: 0px;font-size: 8pt;padding-bottom: 0px;margin: 0em;overflow: visible;width: 100%;color: black;border-top-style: none;line-height: 12pt;padding-top: 0px;font-family: consolas, 'Courier New', courier, monospace;border-right-style: none;border-left-style: none;background-color: white;border-bottom-style: none"><span style="color: #606060"> 3:</span> .With<YourCustomConvention>();</pre>
StructureMap will call a method on YourCustomConvention for each Type it finds, allowing you to do fancy things such as:
1: if (CanBeCast(typeof (IController), type))
<pre style="padding-right: 0px;padding-left: 0px;font-size: 8pt;padding-bottom: 0px;margin: 0em;overflow: visible;width: 100%;color: black;border-top-style: none;line-height: 12pt;padding-top: 0px;font-family: consolas, 'Courier New', courier, monospace;border-right-style: none;border-left-style: none;background-color: #f4f4f4;border-bottom-style: none"><span style="color: #606060"> 2:</span> {</pre>
<pre style="padding-right: 0px;padding-left: 0px;font-size: 8pt;padding-bottom: 0px;margin: 0em;overflow: visible;width: 100%;color: black;border-top-style: none;line-height: 12pt;padding-top: 0px;font-family: consolas, 'Courier New', courier, monospace;border-right-style: none;border-left-style: none;background-color: white;border-bottom-style: none"><span style="color: #606060"> 3:</span> <span style="color: #0000ff">string</span> name = type.Name.Replace(<span style="color: #006080">"Controller"</span>, <span style="color: #006080">""</span>).ToLower();</pre>
<pre style="padding-right: 0px;padding-left: 0px;font-size: 8pt;padding-bottom: 0px;margin: 0em;overflow: visible;width: 100%;color: black;border-top-style: none;line-height: 12pt;padding-top: 0px;font-family: consolas, 'Courier New', courier, monospace;border-right-style: none;border-left-style: none;background-color: #f4f4f4;border-bottom-style: none"><span style="color: #606060"> 4:</span> registry.AddInstanceOf(<span style="color: #0000ff">typeof</span> (IController), <span style="color: #0000ff">new</span> ConfiguredInstance(type).WithName(name));</pre>
<pre style="padding-right: 0px;padding-left: 0px;font-size: 8pt;padding-bottom: 0px;margin: 0em;overflow: visible;width: 100%;color: black;border-top-style: none;line-height: 12pt;padding-top: 0px;font-family: consolas, 'Courier New', courier, monospace;border-right-style: none;border-left-style: none;background-color: white;border-bottom-style: none"><span style="color: #606060"> 5:</span> }</pre>
This allows us, in our MVC application to register all of our controllers using their short name (i.e. LoginController -> “login”) so that they can be loaded via our custom StructureMapControllerFactory.
On-the-fly Dependency Satisfaction
This one comes in handy, but I feel a little dirty when I use it — as though I’m cheating IoC somehow. It’s cool nonetheless:
I have a service that takes the current HttpContext from ASP.NET in order to function. I then needed to pass this service into yet another service in order to construct it properly. There are a couple different ways of accomplishing this, but for this example, I’ll do it this way just to illustrate the point:
1: ObjectFactory
<pre style="padding-right: 0px;padding-left: 0px;font-size: 8pt;padding-bottom: 0px;margin: 0em;overflow: visible;width: 100%;color: black;border-top-style: none;line-height: 12pt;padding-top: 0px;font-family: consolas, 'Courier New', courier, monospace;border-right-style: none;border-left-style: none;background-color: #f4f4f4;border-bottom-style: none"><span style="color: #606060"> 2:</span> .With<ISecurityContext>(<span style="color: #0000ff">new</span> HttpSecurityContext(HttpContext.Current))</pre>
<pre style="padding-right: 0px;padding-left: 0px;font-size: 8pt;padding-bottom: 0px;margin: 0em;overflow: visible;width: 100%;color: black;border-top-style: none;line-height: 12pt;padding-top: 0px;font-family: consolas, 'Courier New', courier, monospace;border-right-style: none;border-left-style: none;background-color: white;border-bottom-style: none"><span style="color: #606060"> 3:</span> .GetInstance<AuthenticationService>();</pre>
In order to get my AuthenticationService instance, I needed an ISecurityContext — particularly the HttpSecurityContext which takes the current HttpContext.
More to Come
As I learn more I’ll post ’em. Also, I think Jeremy has a few more tricks up his sleeve before the release so keep an eye on his blog and the StructureMap web site for more news.
