StructureMap 3 was released recently, and as a regular user of StructureMap, so far I have to say that I'm pleased.
Mainly because not much was really broken, and also because I'm just happy to see a project I use so much continue to get updated.
There are a couple things that are moved around or renamed. In this post, I'll be talking about HTTP request scoping, decoration, and assembly scanning.
StructureMap 3 no longer includes anything tied to HttpContext. But don't panic! It's all been moved to another package: StructureMap.Web. The API is a little different as well. Compare and contrast:
Also note that the cleanup (which I typically put in Application_EndRequest in Global.asax.cs) is also a bit different. In StructureMap 2, I would just call ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();. In StructureMap 3, instead I call HttpContextLifecycle.DisposeAndClearAll();.
If you are using the decorator pattern, then you probably often wire it up in your IoC container. In StructureMap 2, you used EnrichWith. In StructureMap 3, you use DecorateWith. I also think, but I'm not 100% sure, that DecorateWith is a little smarter about types as well. which I only noticed in some of my Castle DynamicProxy stuff. Behold:
If you have my book, AOP in .NET, then you'll know that a change to "EnrichWith" is fairly significant to the code examples in that book. But luckily, it's a relatively easy change (or you can just keep using StructureMap 2.x).
Not a big deal here, just note that some of the assembly scanning extensions (specifically TheCallingAssembly, AssembliesFromApplicationBaseDirectory, AssebliesFromPath) have moved to the StructureMap.Graph namespace. No biggy, especially since ReSharper is able to point these things out and add "using StructureMap.Graph" automatically.
StructureMap 3 is definitely worth a shot. Based on the NuGet numbers so far, it's not being installed en masse just yet. But based on how I use StructureMap, it's practically a no-brainer to make the upgrade. One thing I didn't mention above (because it's not an API change) is that the error messages in StructureMap are now way more useful, and there are other benefits (like performance) that you are essentially getting for free (in terms of the time you'll spend upgrading). The only drawback is that documentation (as always) is lagging, but between the unit tests, Stack Overflow, and just noodling around, I haven't found that to be much of an impediment to an otherwise splendid tool.
I was recently demoing the project to my partner on the staging site (Azure), and I ran into a troubling error having to do with data connections (that I had not experienced running it locally on IIS yet). I immediately thought that it must be an issue with the SQL connections (SqlDbConnection objects that implement the IDbConnection interface, per Dapper) not being closed and/or disposed of correctly.
I remember encountering the same issue when I was developing this very site that you are reading, and the solution is to use the HttpContext scope within StructureMap when configuring it for the IDbConnection interface.
Here's what I started with:
And here's what I refactored it to:
I just added HttpContextScoped (the default is PerRequest scope, see StructureMap docs on Scoping and Lifecycle Management). I also added the ReleaseAndDisposeAllHttpScopedObjects to Global.asax.cs, so that StructureMap would...release and dispose those objects at the end of each request.
However, when I did that, I was still getting problems. I would get exceptions saying that the SqlConnection ConnectionString was the empty string. Something wasn't right. I did some searching around about the issue in the docs, and couldn't really find what I was looking for. I did come across someone with a similar issue on StackOverflow, also trying to understand HttpContextScoped. The top answer from PHeiberg (not the accepted answer) pointed me the right direction.
I used a lambda expression instead of just passing the SqlConnection directly. So now my SqlDbConnections are scoped to the HttpContext, and will be cleaned up after every request. That should squash the issue I was seeing on the staging stie.
Ack, WebForms! Burn it with fire!
WebForms is known to be a horrible ghetto full of HTML pitfalls, a Kafka-esque lifecycle, testability nightmares, and pages served up with inflated ViewState data.
Some of that is still true, but WebForms has improved, and despite all the horrors that we're familiar with, it's still got a lot going for it, including a control tree that can be very useful at times.
I have been working on a WebForms project in my independent consulting practice (it sounds so fancy when I say it like that). Even though it's a relatively small project, I was still thinking about how best to introduce dependency inversion [PDF] to the project (or if I even should).
I googled around, sifted through a lot of ancient blog posts, and I came up with a relatively simple solution with my favorite IoC tool, StructureMap. It's not really optimal, or terribly elegant, but it just might get the job done and still allow us to write tests. I'm certainly open to alternatives, but because this is a small project, I don't want to spend a ton of billable hours on a solution, especially when I'll be turning the code over to novice programmer(s) when my time is done.
First, I simply add standard StructureMap config to the project. Application_Start in Global.asax is as good a place as any. I also use the default convention (if you aren't familiar, it's basically naming the interface by prepending an "I" to the concrete class name).
Okay, so that's pretty standard. Now let's approach from the other end: an actual code-behind class of an ASPX page. I can't use constructor injection here, because I don't have much control over this object being instantiated. Instead, I'll just make public properties for my service(s).
So now I've got a StructureMap defintion, and I've got properties that want to be set by StructureMap. But how do I get them talking? My solution was to introduce a base class (bleck) to the WebForms page. In this constructor, I'll use StructureMap's BuildUp method to populate properties (I have to specify which ones, as noted later on).
That's the ugliest part, as far as I'm concerned, but I couldn't think of a better solution. Maybe an HttpModule or something? Maybe the newer WebForms releases contain something that I'm not familar with yet? Leave a comment with your suggestions.
Finally, StructureMap just won't go and set every property by itself. You need to specify which ones. There are a couple of ways to do this. One is the SetAllProperties, which allows you to conditionally examine the PropertyInfo, and the other is FillAllPropertiesOfType, which you can use to directly specify which properties to set based on what type they are. Even though it's a small project, I'd would prefer to use SetAllProperties, so I don't have to go back and edit my StructureMap config each time I create a new service or a new WebForms page.
Here's a quick example of how to write a caching aspect with Castle DynamicProxy.
First, let's write an implementation and a service worth caching:
Next, you need to decide where to cache the results. If you're using a web app, maybe try ASP.NET's Cache object. If you're writing an Azure app, try AppFabric caching. You can cache things in a database, a text file, whatever is appropriate for your application.
For this simple example, I'm going to cache everything in memory, in a static Dictionary object. Also, nothing that goes into my cache will ever expire or be invalidated. Once it's cached, it's cached forever. Also, it's not thread safe. Not a very useful cache in a real app, so let me just be clear: do not use this in a production application. It's only for demonstration: whatever you use for caching is up to you, I'm just demonstrating the aspect part.
I'm generating the cache key by appending the argument values to the method name. So if you call MyMethod("test") and MyMethod("test2"), that's two different keys that will cache two different results. This might work for you, or you might need a more complex GenerateCacheKey method (something that uniquely identifies objects, perhaps).
Next, I wire up my IoC container to return MyService when asked for an implementation of IMyService. But I also have to make sure that my IoC container (StructureMap in this case) applies the caching interceptor to it.
I put it all together in a simple console app:
And here's the result:
When deploying this site for the first time and actually telling people to come here, I learned that I had made a pretty big mistake. Appropriately (or perhaps tragically), I made a mistake in one of the cross-cutting concerns of my own site.
I am using StructureMap to wire up all my dependencies, and 90% of these dependencies are just done WithDefaultConventions. But there is one dependency, IDbConnection which I wired up by hand, like so:
If you can spot the mistake here, then congratulations, you are officially a better developer than me (which is no high accolade). Here's what I should have done:
And then, in Application_EndRequest, I just call ReleaseAndDisposeAllHttpScopedObjects() on ObjectFactory. This means each of the SqlConnections will be bound to a request, and then disposed of when the request is complete. Because I wasn't doing that before, each of the connections would stay open. I didn't notice this when developing the site, because I was continually rebuilding and redeploying.
This may not be strictly speaking AOP, but it's certainly a cross-cutting concern, as almost every request makes use of the IDbConnection to use Dapper, and using StructureMap to manage that dependency saves me from writing a bunch of boilerplate "new SqlConnection" in all my repositories.
Lesson learned: if this site was actually mission critical to a business, I would've been in hot water. I should've been testing with a larger number of requests over a longer running application in an environment similar to production. Fortunately, since I didn't have a bunch of repetitive boilerplate, it was very easy for me to make the fix, once I figured out what the problem was.
Matthew D. Groves lives in Central Ohio. He works remotely, loves to code, and is a Microsoft MVP.