The 2.5 release of StructureMap is at least 6 weeks away, but I’d like to throw some more stuff out to get some feedback while I’ve got plenty of time to deviate. I’m building in much more interception capabilities into StructureMap to support runtime AOP and other scenarios. I don’t have a good background in AOP, so I’m guessing as much as anything about what I need. Here’s a rundown of what I’ve got so far. The code is targeted to .Net 2.0. My plan is to release for 2.0, then immediately turn around and do a .Net 3.5 release. So please mentally substitute in lambda expressions for the anonymous delegates. All of this code is in the StructureMap trunk, and I’m running my development project off of the current revision.
Intercept a particular instance being created
When a particular instance is created you want to do something with that instance before it’s returned from StructureMap. The configuration code is below:
registry.ForRequestedType<IService>()
.AddInstance(
Instance<IService>().UsingConcreteType<ColorService>()
.OnCreation<ColorService>(delegate(ColorService s) { _lastService = s; })
);
You just pass a delegate or lambda expression to the OnCreation() method like this:
public delegate void StartupHandler<T>(T target);
I envision this being used mostly to run extra bootstrapping or register objects with event brokers. Now, if you want to enrich the object with some sort of runtime AOP or wrap it with a decorator, you would do this:
registry.ForRequestedType<IService>()
.AddInstance(
Instance<IService>().UsingConcreteType<ColorService>()
.EnrichWith<IService>(delegate(IService s) { return new DecoratorService(s); })
)
Again, you just pass in a delegate or lambda into the EnrichWith() method, but this time you’re required to return an object. The delegate signature is this:
public delegate T EnrichmentHandler<T>(T target);
It might be tedious to define these handlers per instance, so let’s move on to broader categories.
Intercept every instance of a requested type
It’s basically the same mechanics, except that you register the EnrichmentHandler or StartupHandler on the ForRequestedType<T> (BuildInstancesOf<T>) expression like these examples:
_registry.ForRequestedType<IService>()
.OnCreation(delegate(IService s) { _lastService = s; })
.AddInstance(
ConstructedBy<IService>(delegate { return new ColorService("Green"); })
.WithName("Green"))
;
_registry.ForRequestedType<IService>()
.EnrichWith(delegate(IService s) { return new DecoratorService(s); })
.AddInstance(
ConstructedBy<IService>(delegate { return new ColorService("Green"); })
.WithName("Green"))
;
The first snippet directs StructureMap to execute a delegate against every new instance of IService created. The second snippet would give you the ability to replace any new instance of type IService created with an enriched type or decorator.
All of these examples so far have worked on an object created by StructureMap shortly after it was created. Now, what if you want to intercept the request itself and return something entirely different depending on circumstances? For a couple years, StructureMap has an extensibility mechanism called an InstanceFactoryInterceptor that acts much as an IHttpModule in ASP.Net to decorate the normal InstanceFactory object that builds a given family of instances. I’ve used that mechanism to create the “scoping” feature that allows you to make instances either singleton’s or thread local or HttpContext specific. As I said, it’s been a feature for awhile, but never documented and got cut from the original release of the fluent interface. I’m fixing that with the 2.5 release like so:
AnInstanceFactoryInterceptor factoryInterceptor = new AnInstanceFactoryInterceptor();
PluginGraph pluginGraph = new PluginGraph();
using (Registry registry = new Registry(pluginGraph))
{
registry.BuildInstancesOf<IGateway>().InterceptConstructionWith(factoryInterceptor);
}
I suppose you could use this to register an AOP handler, but I see this primarily as a way to do instance caching or switch out the actual instances returned by some sort of security rules. You might use this to return a read only implementation of IView for some roles, and an editable implementation of IView for other roles.
Intercept any instance of any type that meets this criteria
Okay, now we come to the feature that’s very specifically intended for AOP. What if you want your runtime AOP engine to intercept any type that’s decorated with the magic AOP attributes? You could implement a new class that implements the TypeInterceptor interface shown below:
public interface InstanceInterceptor
{
object Process(object target);
}
public interface TypeInterceptor : InstanceInterceptor
{
bool MatchesType(Type type);
}
You just need to implement two methods. MatchesType(Type) can look at a .Net Type and determine if the TypeInterceptor can act upon an instance. The Process() method acts as the EnrichmentHandler delegate to return any kind of decorator or runtime AOP proxy instead of the original instance. You would register TypeInterceptor like this:
MockTypeInterceptor interceptor = new MockTypeInterceptor();
registry.RegisterInterceptor(interceptor);
// or
StructureMapConfiguration.RegisterInterceptor(interceptor);
You can register more than one TypeInterceptor. The result of MatchesType() is cached by Type, so subsequent creations of a concrete class will not have to call expensive reflective calls to match the types.
Well, waddaya think? Useful, or do you need something else entirely?