4

動作の依存関係を注入する代わりに、データの依存関係を注入したいと思います。データはデータベースまたは他のサービス呼び出しから取得されます。これを実現するための最初のステップは、タイプ間バインディングではなく、タイプ間バインディングを作成することです。これまでのところ、ほとんどの.netIOCコンテナでそれができると思います。

ただし、データストアへのラウンドトリップが多くなるため、多くのデータインジェクションによって不要なレイテンシが発生することが予想されます。むしろ、注入されるすべてのデータオブジェクトが単一のリクエストでフェッチされることを望んでいます。

このためのフレームワークやライブラリ、またはサンプルコードがあるかどうか知りたいのですが。

フレームワークまたはライブラリは、デリゲートがバインドするように以前の提案を進化させることによって、私のニーズを満たすと思います...

  1. それぞれがデータ要求をスレッドローカルストレージキューに配置します。
  2. であるタイプのみを返しますLazy< T >

IOCコンテナがすべてのタイプの注入を完了した後、Lazy< T >プログラムの実行は常にLazy< T >.Valueデリゲートの1つを実行します。その場合、データ要求のキュー全体が同時に送信され、TLS内のハッシュテーブルまたはディクショナリに同時に入力されます。したがって、最初のLazy< T >.Valueヒットは遅くなりますがLazy< T >、IOCコンテナのインジェクションツリーの一部であった他のすべてのアイテムはすぐに実行されます。

この提案されたアプローチについての考えは?どんな図書館もこのようなことをしますか?

4

1 に答える 1

3

データを必要とするコンシューマーに直接データを注入することは、状況によっては機能する場合がありますが、ほとんどの場合、このデータをクエリする(提供された引数を使用してデータを要求する)ことにより、必要なデータを決定するのはコンシューマーです。

データを直接注入できる(パラメーターのないクエリとして表示できる)いくつかのケースでは、データを直接注入すると、DI構成が非常に複雑になります。たとえば、システムの現在の時刻に依存する消費者を考えてみましょう。DateTimeをコンシューマー(または)に注入することができLazy<DateTime>ます。別の消費者は現在のユーザーの生年月日を必要とする可能性があるため、この消費者もに依存しますDateTime。しかし、DateTime依存関係には2つの意味があるため、システムにあいまいさが生じます。DIコンテナーはこれを処理するのが苦手であり、これを解決するには、コンテナーDateTimeを必要とする各コンシューマーにとって何を意味するかをコンテナーに明示的に指示する必要があります。これにより、構成が脆弱になり、封じ込めが困難になります。

この後者の場合(パラメーターなしのクエリ)の解決策は、解決できる明確なインターフェースを定義することです。上記の例では、ITimeProviderインターフェースとインターフェースを定義できIUserContextます。各コンシューマーは、正しいインターフェースに依存することができます。

前者の場合(パラメーター化されたクエリ)、このためのフレームワークは必要ありません。適切な設計が必要です。

あなたはデータベースとWebサービスのクエリについて話していて、返されたデータをキャッシュする方法が必要です。したがって、必要なのはクエリを定義するための抽象化であり、キャッシュやその他の横断的関心事をプラグイン可能でコードに変更を加える必要がない方法で適用できるようにします。

これを行う効果的な方法は、クエリオブジェクト(クエリの入力パラメータ)+リターンタイプを定義するインターフェイスと、そのクエリを処理するロジックを定義するインターフェイスを定義することです。

public interface IQuery<TResult>
{
}

public interface IQueryHandler<TQuery, TResult> 
    where TQuery : IQuery<TResult>
{
    TResult Handle(TQuery query);
}

これらの抽象化を使用すると、次のようなクエリオブジェクトを定義できます。

public class FindUsersBySearchTextQuery : IQuery<User[]>
{
    public string SearchText { get; set; }
    public bool IncludeInactiveUsers { get; set; }
}

このオブジェクトは、検索テキストによってユーザーを検索し、非アクティブなユーザーを含めたり除外したりできるクエリを定義します。クエリ結果はUserオブジェクトの配列です。

このクエリを実行するロジックは、次のように実装できます。

public class FindUsersBySearchTextQueryHandler
    : IQueryHandler<FindUsersBySearchTextQuery, User[]>
{
    private readonly NorthwindUnitOfWork db;

    public FindUsersBySearchTextQueryHandler(
        NorthwindUnitOfWork db)
    {
        this.db = db;
    }

    public User[] Handle(FindUsersBySearchTextQuery query)
    {
        return (
            from user in this.db.Users
            where user.Name.Contains(query.SearchText)
            where user.IsActive || query.IncludeInactiveUsers
            select user)
            .ToArray();
    }
}

そして、なぜこれがあなたが抱えている問題を正確に解決するのですか?これにより、コンシューマーはインターフェースに依存できるため、問題は解決しますが、誰も知らなくても、実装をデコレーターIQueryHandler<FindUsersBySearchTextQuery, User[]>でラップできます。結果をトレッドローカルストゲージにキャッシュするデコレータを作成するのは簡単です。IQueryHandler<T>

public class TlsCachingQueryHandlerDecorator<TQuery, TResult>
    : IQueryHandler<TQuery, TResult>
    where TQuery : IQuery<TResult>
    where TResult : class
{
    [ThreadStatic]
    private static TResult cache;

    private readonly IQueryHandler<TQuery, TResult> decorated;

    public ValidationQueryHandlerDecorator(
        IQueryHandler<TQuery, TResult> decorated)
    {
        this.decorated = decorated;
    }

    public TResult Handle(TQuery query)
    {
        return cache ?? (cache = this.decorated.Handle(query));
    }
}

ソリッドDIコンテナを使用すると、これらのデコレータを登録し、(装飾されたタイプのタイプ情報に基づいて)条件付きでデコレータを登録できます。これにより、このキャッシングデコレータを(条件に応じて)安全にキャッシュできるタイプにのみ配置できます。

このモデルの詳細については、この記事を参照してください。一方、私のアーキテクチャのクエリ側で

于 2012-10-30T09:25:26.110 に答える