2

を介して非同期プロセスを生成するループがいくつかありますConcurrentQueue<T>。これらのプロセスは、データベースの対話にリポジトリを使用するいくつかのビジネス サービスの実装を呼び出します。サービスの実装はすべて、StructureMap を介して接続されています。

リポジトリの実装には、注意深い管理が必要な特性がいくつかあります。

  • 使用されているデータベース技術はRedisです。
  • この実装では、ServiceStack.net の Redis クライアントを ( 経由でPooledRedisClientManager) 使用します。
  • いくつかのメソッドは、同じ RedisClient によって同時に作成できない (私が信じている) トランザクションを利用します。したがって、複数のトランザクションを同時に作成しようとするため、複数の非同期プロセスで単一のリポジトリ実装を共有することはできません。
  • メモリとデータベース接続を解放するには、これらのオブジェクトを明示的に破棄する必要があります。そのため、リポジトリ クラスに IDisposable を実装しました。
  • 同時要求/トランザクションがないため、個々の非同期プロセスの範囲内でリポジトリ インスタンスを共有できない理由はありません。

上記を念頭に置いて、単一のリポジトリ インスタンスのスコープを各非同期プロセスの有効期間に設定したいと思います。

覚えておくべきことの 1 つは、非同期プロセスのスコープで使用されるサービスは、異なる有効期間特性を持つシステムの他の部分でも使用されるということです (たとえば、リポジトリがページの有効期間にスコープされている Web サイト内)。リクエスト)。

いくつかのコード(私のコードから簡略化したもの)で説明しようとします:

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 に指示できますか? またはコードで明示的にこれを行う必要がありますか?
4

1 に答える 1

1

このような状況では、ネストされたコンテナを使用できます。

ネストされたコンテナーは、作成するすべての一時オブジェクトを追跡します。ネストされたコンテナー自体が破棄されると、作成した一時オブジェクトのいずれかで 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>();
于 2012-03-11T17:41:43.453 に答える