【问题标题】:StructureMap: Custom Lifetime Scoping Within Specific ContextStructureMap:特定上下文中的自定义生命周期范围
【发布时间】:2012-03-06 12:53:36
【问题描述】:

我有几个循环,每个循环都通过ConcurrentQueue<T> 生成异步进程。这些流程调用一些业务服务实现,它们使用存储库进行数据库交互。服务实现都是通过 StructureMap 连接起来的。

存储库实现具有一些需要仔细管理的特性:

  • 使用的数据库技术是Redis
  • 该实现使用 ServiceStack.net 的 Redis 客户端(通过 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. 我是否可以通过 DSL 配置指示 StructureMap 在流程结束时处置存储库,还是需要在我的代码中明确执行此操作?

【问题讨论】:

    标签: c# scope structuremap servicestack


    【解决方案1】:

    对于这种情况,您可以使用nested container

    嵌套容器将跟踪它的所有瞬态对象 创建。当嵌套容器本身被释放时,它会调用 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>();
    

    【讨论】:

    • 当我尝试使用嵌套容器方法时遇到事务冲突(换句话说,多个进程同时使用同一个存储库) - 我将存储库设置为 @ 987654324@。为嵌套容器使用单独的命名配置文件可以解决这个问题吗?
    • @Zac 没有看到代码很难说。但是,如果您为每个线程创建新的嵌套容器并在没有任何生命周期的情况下注册存储库,那么就不应该有任何冲突。我不知道你为什么有 ItemManager 和 ItemProcessor。他们似乎做着完全相同的事情。我可能只有 ItemProcessor.Process() 来启动线程、处理项目并处理嵌套容器。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-21
    • 2017-11-08
    • 2011-01-22
    • 2021-01-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多