2
votes

I have a couple of loops which each spawn asynchronous processes via a ConcurrentQueue<T>. These processes call some business service implementations which use a repository for database interactions. The service implementations are all wired-up via StructureMap.

The repository implementation has some characteristics which require careful management:

  • The database technology being used is Redis.
  • The implementation uses ServiceStack.net's Redis client (via a PooledRedisClientManager).
  • Several methods make use of transactions which (I believe) cannot be created concurrently by the same RedisClient. Therefore, sharing a single repository implementation over multiple asynchronous processes is not possible as it would attempt to create multiple transactions concurrently.
  • In order to free up memory and database connections these objects need to be explicitly disposed - as such I've implemented IDisposable on the repository class.
  • There is no reason why a repository instance can't be shared within the scope of an individual asynchronous process because there will be no concurrent requests/transactions.

With the above in mind, I'd like to scope a single repository instance to the lifetime of each asynchronous process.

One thing to keep in mind is that the services used with the scope of the async processes are also used by other parts of the system which have different lifetime characteristics (for example, within a website where the repository is scoped to the lifetime of a page request).

I'll try to illustrate with some code (simplified from my code):

The program managing the queue (and wiring the event handler which executes the IAsyncResult invocation):

public class Program
{
    private readonly ConcurrentQueue<QueueItem> queue = new ConcurrentQueue<QueueItem>();
    private readonly IItemManager itemManager; // implemented via constructor DI.

    public void ProcessQueueItems()
    {
        while ( queue.Count > 0 || shouldContinueEnqueuing )
        {
            QueueItem item;
            if ( queue.TryDequeue( out item ) )
            {
                // Begin async process here - only one repository should be used within the scope of this invocation
                // (i.e. withing the scope of the itemManager.ProcessItem( item ) method call.
                new ItemProcessor( itemMananger.ProcessItem ).BeginInvoke( e.Item, ItemCallback, null );
            }

            Thread.Sleep( 1 );
        }

    }

    private static void ItemCallback( IAsyncResult result )
    {
        var asyncResult = ( AsyncResult ) result;
        var caller = ( ItemProcessor ) asyncResult.AsyncDelegate;

        var outcome = caller.EndInvoke( result );

        // Do something with outcome...
    }

    private delegate ItemResult ItemProcessor( QueueItem item );
}

The implementation which is called by the asynchronous result. The I want to manage the scope within the ProcessItem( ... ) method:

public class ItemManager : IItemManager
{
    private readonly IServiceA serviceA; // implemented via constructor DI.
    private readonly IServiceB serviceB; // implemented via constructor DI.

    public ItemResult ProcessItem( QueueItem item )
    {
        // Both serviceA and serviceB use the repository which is injected via StructureMap. They should share 
        // the instance and at the end of the process it should be disposed (manually, if needs be).
        var something = serviceA.DoSomething( item );

        return serviceB.GetResult( something );
    }
}

I think that explains the situation and goals. My questions are as follows:

  1. Can I use StructureMap to use a different scope within the context of a single process as described above.
  2. I don't want to include a direct dependency for StructureMap in my domain/service layer. So, if I am able to use a different scope at this stage, is there an easy way to do this without directly invoking StructureMap from within the process itself?
  3. Can I instruct StructureMap, via DSL configuration, to dispose the repository at the end of the process or do I need to explicitly do this in my code?
1

1 Answers

1
votes

You could use nested container for this kind of situation.

The nested container will track all of the transient objects that it creates. When the nested container itself is disposed, it will call Dispose() on any of the transient objects that it created.

var nestedContainer = container.GetNestedContainer();
var processor = nestedContainer.GetInstance<IItemProcessor>();

Other way to make sure every object uses same repository is to use the With() method

// Get the IRepository which should be shared   
// This object is registered using simple
// For<ISession>.Use<Session> registration so not scoped
// http context or anything like that
var session = container.GetInstance<ISession>();

// Create instance of IProcessor using the specific instance
// of ISession. If multiple classes in the object grap use ISession
// they will get the same instance. Note that you can use multiple
// With() statements
var itemProcessor = container.With(session).GetInstance<IItemProcessor>();