mikeobrien.net Curriculum Vitae Blog Labs
Tuesday, January 12, 2010

StructureMap is an excellent IoC tool; very powerful and easy to use. One thing that I think is lacking though is a way to create a custom lifecycle “strategy” (For lack of a better word). Currently you can specify a lifecycle and you can use one the hybrid lifecycles but this isn’t sufficient in some circumstances. In our code base we have ASP.NET web pages, WCF services, Windows services and console applications (And who knows what next, MVC?) and we have one registry for all these applications. We’re using NHibernate so we have different lifecycle needs for ISession in each type of application. Another side of a lifecycle is the end of it. We want to be able to explicitly end a lifecycle in our ASP.NET and WCF apps. So this is what I mean by a “strategy”.

Solution

One thing that’s really nice about StructureMap is that it’s really easy to extend. Jeremy et al have done a great job of creating a codebase that’s really easy to dive into and understand. So the following is a strategic lifecycle API that allows you to define a lifecycle strategy. I’ll first explain how to use it and then further down I’ll explain the code under the covers. Now I do want to give the disclaimer that I’m no StructureMap expert or anything, so there may be better or more “correct” ways to do this. If so, be sure to leave a comment if you know.

You can download the code here. The code that matters is in the StructureMapContrib project.

Lifecycle strategies can be defined with the LifecycleStrategiesAre() method which takes any number of ILifecycleStrategy parameters:

ObjectFactory.Configure(
    config => config.For<INumberGenerator>().
              LifecycleStrategiesAre(...).
              Use<NumberGenerator>());

This is simply a convenience extension method for the following:

ObjectFactory.Configure(
    config => config.For<INumberGenerator>().
              LifecycleIs(new StrategicLifecycle(...)).
              Use<NumberGenerator>());

StrategicLifecycle implements ILifecycle and contains the different lifecycle strategies. Now a LifecycleStrategy can take three pieces of information; first, a lambda that determines if the lifecycle is valid (If this is not specified it defaults to true). Second, a lambda that lazily creates the lifecycle. And third, a lambda that will deterministically dispose of the object cache (If this is not specified then the object cache is not deterministically disposed). The following is a strategy defined for ASP.NET pages:

new LifecycleStrategy(
    () => HttpContextLifecycle.HasContext() && HttpContext.Current.Handler is Page, 
    () => new HttpContextLifecycle(), 
    c => ((Page)HttpContext.Current.Handler).Unload += ((s,e) => c.DisposeAndClear())),

This strategy is only valid when there is a current HttpContext and when the handler is a Web.UI.Page. In this case we would use StructureMap’s HttpContextLifecycle. And we want to deterministically dispose of the object cache by hooking into the page Unload event. We can define other strategies to create a full lifecycle strategy as follows:

ObjectFactory.Configure(
    config => config.For<INumberGenerator>().
              LifecycleStrategiesAre(
                  new LifecycleStrategy(
                        () => HttpContextLifecycle.HasContext() && HttpContext.Current.Handler is Page, 
                        () => new HttpContextLifecycle(), 
                        c => ((Page)HttpContext.Current.Handler).Unload += ((s,e) => c.DisposeAndClear())),

                  new LifecycleStrategy(
                        WcfOperationLifecycle.HasContext, 
                        () => new WcfOperationLifecycle(), 
                        c => OperationContext.Current.OperationCompleted += ((s, e) => c.DisposeAndClear())),

                  new LifecycleStrategy(() => new ThreadLocalStorageLifecycle())).
              Use<NumberGenerator>());

Here we have a strategy that defines lifecycles for ASP.NET pages, WCF services and a fallback or default strategy of thread local storage. BTW, the WcfOperatonLifecycle is explained here. Notice that the thread local storage doesn't specify if it is valid or an explicit dispose handler. Since it’s a fall back strategy it is listed last and is always valid and the object cache will not be deterministically disposed. This registration looks a bit cluttered so I’ve encapsulated these strategies into their own classes which cleans things up a bit:

ObjectFactory.Configure(
    config => config.For<INumberGenerator>().
              LifecycleStrategiesAre(
                  new HttpContextAndPageLifecycleStrategy(),
                  new WcfOperationLifecycleStrategy(),
                  new LifecycleStrategy(() => new ThreadLocalStorageLifecycle())).
              Use<NumberGenerator>());

And that’s it. Now below, if you’re interested, we’ll examine the code behind this.

Implementation

It all starts with the StrategicLifecycle class:

public class StrategicLifecycle : ILifecycle
{
    private IEnumerable<ILifecycleStrategy> _strategies;

    public StrategicLifecycle(params ILifecycleStrategy[] strategies)
    { _strategies = strategies; }

    public void EjectAll()
    { ResolveLifecycle().EjectAll(); }

    public IObjectCache FindCache()
    { return ResolveLifecycle().FindCache(); }

    public string Scope
    { get { return "StrategicLifecycle"; } }

    private ILifecycle ResolveLifecycle()
    {
        var lifecycle = (from strategy in _strategies
                         where strategy.IsValid()
                         select strategy.Lifecycle).FirstOrDefault();
        if (lifecycle == null) throw
            new Exception("Unable to find a suitable lifecycle Strategy.");
        return lifecycle;
    }
}

This lifecycle dynamically chooses a valid lifecycle by calling the IsValid() method on all the ILifecycleStrategy’s passed in. The first one it finds is the winner.

Lifecycle strategies implement the ILifecycleStrategy interface which only has two members; IsValid() to determine it the lifecycle is valid in a particular circumstance and the Lifecycle property to return an instance of a lifecycle:

public interface ILifecycleStrategy
{
    bool IsValid();
    ILifecycle Lifecycle { get;}
}

This is the implementation of ILifecycleStrategy that I’m using:

public class LifecycleStrategy : ILifecycleStrategy
{
    private Func<bool> _isValid;
    private ILifecycle _lifecycle;
    private Func<ILifecycle> _create;
    private Action<IObjectCache> _dispose;

    public LifecycleStrategy(Func<ILifecycle> create, Action<IObjectCache> dispose) : this(() => true, create, dispose) { }
    public LifecycleStrategy(Func<bool> isValid, Func<ILifecycle> create) : this(isValid, create, null) { }
    public LifecycleStrategy(Func<ILifecycle> create) : this(() => true, create, null) { }

    public LifecycleStrategy(Func<bool> isValid, Func<ILifecycle> create, Action<IObjectCache> dispose)
    {
        _isValid = isValid;
        _create = create;
        _dispose = dispose;
    }

    public bool IsValid()
    {
        return _isValid();
    }

    public ILifecycle Lifecycle
    {
        get
        {
            if (_lifecycle == null)
                if (_dispose == null) _lifecycle = _create();
                else _lifecycle = new DisposableLifecycleProxy(_create(), _dispose);
            return _lifecycle;
        }
    }
}

This is simply a container for the lambdas passed in and for the lifecycle. The lifecycle is created lazily and cached. You’ll notice that if a deterministic dispose lambda is passed, the lifecycle is wrapped in a DisposableLifecycleProxy class. Here is that class:

public class DisposableLifecycleProxy : ILifecycle
{
    private ILifecycle _lifecycle;
    private Action<IObjectCache> _dispose;
    private Dictionary<IObjectCache, IObjectCache> _objectCaches = 
        new Dictionary<IObjectCache, IObjectCache>();

    public DisposableLifecycleProxy(ILifecycle lifecycle, Action<IObjectCache> dispose)
    {
        _lifecycle = lifecycle;
        _dispose = dispose;
    }

    public void CacheDisposed(IObjectCache objectCache)
    {
        if (!_objectCaches.ContainsKey(objectCache)) return;
        lock (_objectCaches) { _objectCaches.Remove(objectCache); }
    }

    public void EjectAll() { _lifecycle.EjectAll(); }

    public IObjectCache FindCache()
    {
        IObjectCache objectCache = _lifecycle.FindCache();

        // This is here to ensure the close lambda is only executed once
        // per object cache. Not sure of a better way to handle this.
        if (!_objectCaches.ContainsKey(objectCache))
        {
            ObjectCacheProxy proxy = new ObjectCacheProxy(this, objectCache);
            lock (_objectCaches) { _objectCaches.Add(objectCache, proxy); }
            _dispose(proxy);
        }

        return _objectCaches[objectCache];
    }

    public string Scope { get { return _lifecycle.Scope; } }

    // ────────────────────────── Nested Types ──────────────────────────

    public class ObjectCacheProxy : IObjectCache, IDisposable
    {
        private IObjectCache _objectCache;
        private DisposableLifecycleProxy _lifecycleProxy;
        private bool _disposed;

        public ObjectCacheProxy(DisposableLifecycleProxy lifecycleProxy, IObjectCache objectCache)
        {
            _objectCache = objectCache;
            _lifecycleProxy = lifecycleProxy;
        }

        public void DisposeAndClear() 
        {
            _objectCache.DisposeAndClear(); 
            Dispose();
        }

        public void Dispose()
        {
            if (_disposed) return;

            _disposed = true;
            _lifecycleProxy.CacheDisposed(_objectCache);
        }

        public int Count { get { return _objectCache.Count; } }
        public void Eject(Type pluginType, Instance instance) { _objectCache.Eject(pluginType, instance); }
        public object Get(Type pluginType, Instance instance) { return _objectCache.Get(pluginType, instance); }
        public bool Has(Type pluginType, Instance instance) { return _objectCache.Has(pluginType, instance); }
        public object Locker { get { return _objectCache.Locker; } }
        public void Set(Type pluginType, Instance instance, object value) { _objectCache.Set(pluginType, instance, value); }
    }        
}

This is the part I’m not all that fond of. It’s a bit too much acrobatics to do something really simple and I wasn't able to put any more effort into it because of time constraints. But it’s the only way I could figure out to transparently control the lifetime of an object cache. Essentially the dispose lambda is called the first time the FindCache() method is called. This class maintains a dictionary of all the object caches it’s called the dispose lambda on so that it only ever calls it once per object cache. Plus the object cache that is returned is wrapped in a proxy that will signal back that the object cache has been disposed and can be removed from the mapping. I wish there was something that indicated if the cache existed before you called FindCache(). That way you could tell if it was newly created and in this case call the explicit dispose lambda on it. For now this is my workaround.  

The rest of the classes in the StructureMapContrib project are just simple convenience classes and shouldn’t require any explanation.

Tuesday, January 12, 2010 6:04:12 PM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  |  Trackback

We have a RESTful and SOAPy API so having a WCF operation lifecycle in StructureMap was imperative. Creating lifecycles in StructureMap is cake. You can find some examples under the Pipeline namespace in the StructureMap source code. All you do is create a class that implements StructureMap.Pipeline.ILifecycle. Below is a lifecycle that is based on a WCF operation:

public class WcfOperationLifecycle : ILifecycle
{
    public static readonly string ITEM_NAME = "STRUCTUREMAP-INSTANCES";

    public void EjectAll()
    {
        FindCache().DisposeAndClear();
    }

    public IObjectCache FindCache()
    {
        if (!OperationContext.Current.OutgoingMessageProperties.ContainsKey(ITEM_NAME))
            OperationContext.Current.OutgoingMessageProperties.Add(ITEM_NAME, new MainObjectCache());
        return (IObjectCache)OperationContext.Current.OutgoingMessageProperties[ITEM_NAME]; 
    }

    public string Scope { get { return "WcfOperationLifecycle"; } }
}

I used the existing HttpContextLifecycle class as my starting point. The important part of this class is the FindCache() method which locates the object cache. As you can see above we are stashing the object cache in the outgoing message properties. I’m not sure what the significance of the Scope property is other than debugging perhaps, but from what I can see you just return a descriptive string.

Registering this is also very simple; simply specify this as the lifecycle you’d like to use:

ObjectFactory.Configure(
    config => config.For<INumberGenerator>().
              LifecycleIs(new WcfOperationLifecycle()).
              Use<NumberGenerator>());

That’s all there is to it!

Tuesday, January 12, 2010 4:49:09 PM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  |  Trackback
Saturday, January 09, 2010

Jimmy Bogard has written a couple of posts here and here discussing the registering partially closed generic types with StructureMap. These posts helped me solve a problem I was having which is a little bit different. To set the stage let me explain what we’re doing. First we have a generic repository interface:

public interface IRepository<TEntity> : IQueryable<TEntity> where TEntity : class
{
    TEntity Get(Guid id);
    void Save(TEntity entity);
    void Delete(TEntity entity);
    void DeleteSingle(IQueryable<TEntity> query);
    void DeleteMany(IQueryable<TEntity> query);
}

Then we will have an interface for a specific repository that inherits from the generic repository interface and includes convenience methods:

public interface IAccountRepository : IRepository<Account>
{
    void DeleteInactiveAccountsOlderThanTwoYears();
    void CreateAccountBasedOnExistingAccount(Account account);
    // Etc, etc, etc
}

Then we have the concrete class that implements the repository specific interface (The NHibernateRepositoryBase class implements the members defined by IRepository<TEntity> not show below):

public class AccountRepository : NHibernateRepositoryBase<Account>, IAccountRepository
{
    public void DeleteInactiveAccountsOlderThanTwoYears() {...};
    public void CreateAccountBasedOnExistingAccount(Account account) {...};
    // Etc, etc, etc    
}

So what I wanted is to scan our persistence assembly for any concrete types (IE: AccountRepository) that implement an interface (IE: IAccountRepository) that derives from a closed generic interface (IE: IRepository<Account>) that is of a particular open generic interface type (IE: IRepository<>). Clear as mud?? :) Simply put, I want to scan for IRepository<> and want to map IAccountRepository to AccountRepository. This is cake with StructureMap using a registration convention. BTW, I’m using StructureMap 2.5.4 and from what I understand you may need this version to do what I’m doing here.

I lifted the GenericConnectionScanner class (Which is what the ConnectImplementationsToTypesClosing convenience method uses) from the StructureMap code as my starting point and modified the logic:

public class DerivedOpenGenericInterfaceConnectionScanner : IRegistrationConvention
{
    private readonly Type _openType;

    public DerivedOpenGenericInterfaceConnectionScanner(Type openType)
    {
        _openType = openType;
        if (!_openType.IsInterface || !_openType.IsOpenGeneric())
            throw new ApplicationException(
                "This scanning convention can only be used with open generic interface types");
    }

    public void Process(Type type, StructureMap.Configuration.DSL.Registry registry)
    {
        if (!type.IsConcrete()) return;
        var derivedTypes = type.GetInterfaces().
                            Where(i => i.GetInterfaces().
                                    Any(i2 => i2.IsGenericType && 
                                              i2.GetGenericTypeDefinition() == _openType));
        if (derivedTypes.Count() > 0) registry.For(derivedTypes.First()).Add(type);
    }
}

The constructor takes the open generic interface type. The Process method is called for each type; we only want concrete types. We check the type for interfaces that derive from an interface that has a type definition of our open generic interface type. If one exists we map the derived interface type to the concrete type.

ObjectFactory.Initialize(
    x => x.Scan(
        config => {
             config.AssemblyContainingType(typeof(IRepository<>));
             config.With(new DerivedOpenGenericInterfaceConnectionScanner(typeof(IRepository<>)));
         }));

var accountRepo = ObjectFactory.GetInstance<IAccountRepository>();
System.Diagnostics.Debug.Assert(accountRepo != null && accountRepo is AccountRepository);

The above initialization scans the assembly containing our open generic interface type and specifies our registration convention using the With() method. That’s all there is to it!

Saturday, January 09, 2010 3:18:08 AM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  |  Trackback
Creative Commons License