StructureMap: пользовательская область видимости в определенном контексте - PullRequest
2 голосов
/ 06 марта 2012

У меня есть пара циклов, каждый из которых порождает асинхронные процессы через ConcurrentQueue<T>.Эти процессы вызывают некоторые реализации бизнес-сервисов, которые используют хранилище для взаимодействия с базой данных.Все реализации сервиса подключены через StructureMap.

Реализация репозитория имеет некоторые характеристики, которые требуют тщательного управления:

  • Используемая технология базы данных: Redis .
  • Реализация использует Redis-клиент ServiceStack.net (через PooledRedisClientManager).
  • Несколько методов используют транзакции, которые (я считаю) не могут быть созданы одновременно одним и тем же RedisClient.Поэтому совместное использование одной реализации репозитория для нескольких асинхронных процессов невозможно, так как при этом будет пытаться создать несколько транзакций одновременно.
  • Чтобы освободить подключения к памяти и базе данных, эти объекты должны быть явно расположены - как таковые, яреализовал IDisposable в классе репозитория.
  • Нет никаких причин, по которым экземпляр репозитория не может совместно использоваться в пределах области действия отдельного асинхронного процесса, поскольку существуетне будет одновременных запросов / транзакций.

Учитывая вышесказанное, я бы хотел привязать один экземпляр репозитория к времени жизни каждого асинхронного процесса.

Одна вещь дляимейте в виду, что службы, используемые с областью асинхронных процессов, также используются другими частями системы, которые имеют различные характеристики времени жизни (например, на веб-сайте, где хранилище ограничено временем жизни запроса страницы).

Я будупопробуйте проиллюстрировать это некоторым кодом (упрощенно из моего кода):

Программа, управляющая очередью (и подключающая обработчик событий, который выполняет вызов IAsyncResult):

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 );
}

Реализациякоторый вызывается асинхронным результатом.Я хочу управлять областью действия в методе ProcessItem( ... ):

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 );
    }
}

Я думаю, что это объясняет ситуацию и цели.У меня следующие вопросы:

  1. Могу ли я использовать StructureMap для использования другой области действия в контексте одного процесса, как описано выше.
  2. Я не хочу включать прямойзависимость для StructureMap в моем доменном / сервисном слое.Итак, если на этом этапе я могу использовать другую область видимости, есть ли простой способ сделать это без непосредственного вызова StructureMap из самого процесса?
  3. Могу ли я дать через StructureLap через конфигурацию DSL распоряжение утилизироватьхранилище в конце процесса или мне нужно явно сделать это в моем коде?

1 Ответ

1 голос
/ 11 марта 2012

Вы можете использовать вложенный контейнер для такой ситуации.

Вложенный контейнер будет отслеживать все переходные объекты, которые он создает. Когда вложенный контейнер будет удален, он вызовет Dispose () для любого из временных объектов, которые он создал.

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

Другой способ убедиться, что каждый объект использует один и тот же репозиторий, - это использовать метод With ()

// 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>();
...