14

私のアプリケーションには、多数のフロントエンド アセンブリ (Windows サービスと MVC3 Web アプリケーションを含む) によって共有される多数のバックエンド アセンブリ (Entity Framework データ リポジトリ レイヤーを含む) が含まれています。

Ninject バインディング プロセスについての私の理解では、注入可能な型を含む各アセンブリには、これらの型の既定のバインディングを定義する Ninject モジュールも含まれている必要があります。定義されたモジュールのセットは、消費アセンブリの Ninject カーネルに読み込まれます。

ただし、必要なバインド スコープが常に一貫しているわけではないため、問題が発生しています。たとえば、私の MVC プロジェクトは data context にバインドする必要がありますInRequestScopeが、Windows サービスは同じ class にバインドしInThreadScopeます。

すべてのモジュールをフロントエンド プロジェクトに再配置し、使用シナリオごとに各モジュールの個別のコピーを維持することで、この問題を解決できることは明らかですが、複数のプロジェクト間でモジュール コンテンツの多くが複製されるため、これはハックに思えます。

モジュールを多層アプリケーションのどこに配置するかについてのベスト プラクティスはありますか? プロジェクト間の違いをバインドする必要性とこれをどのように調和させることができますか?

ご提案いただきありがとうございます。

ティム

4

2 に答える 2

14

単一のアプリケーションを使用するソリューションの場合、一般的なアドバイスは、コンテナーをアプリケーション プロジェクト (Web アプリまたは Web サービス プロジェクト) に登録することです。Web アプリケーションの場合、これは通常 Global.asax になりますApplication_Start。すべてを結び付けるこの場所は、DI 用語ではコンポジション ルートと呼ばれます。

マルチアプリケーション ソリューションの場合でも、アプリケーション プロジェクトごとに 1 つのコンポジション ルートが存在します。すべてのアプリケーションには独自の構成があるため、これは必須です。一方、重複したコードは常に悪いものです。新しい抽象化を導入するときに、3 つの場所を変更する必要はありません。

秘訣は、すべての登録をプロジェクト階層の下に移動することです。たとえば、ビジネス層アセンブリ (およびその下) に依存する単一の「ブートストラップ アセンブリ」を定義し、変更されないアセンブリのすべての登録を保持できます。その後、アプリケーションのコンポジション ルートは、そのアセンブリを使用して既定の登録を取得し、アプリケーション固有の依存関係でそれを拡張できます。

そのようなことは次のようになります。

// MVC Composition root
public static void Bootstrap()
{
    var container = new Container();

    // Default registrations
    BusinessLayerBootstrapper.Bootstrap(container);

    // Application specific registrations
    container.Bind<IUserContext>().To<AspNetUserContext>();

    DependencyResolver.Current = 
        new ContainerDependencyResolver(container);
}

// Windows Service Composition root
public static void Bootstrap()
{
    var container = new Container();

    // Default registrations
    BusinessLayerBootstrapper.Bootstrap(container);

    // Application specific registrations
    container.Bind<IUserContext>().To<SystemUserContext>()
        .SingleScoped();

    // Store somewhere.
    Bootstrapper.Container = container;
}

// In the BL bootstrap assembly
public static class BusinessLayerBootstrapper
{
    public static void Bootstrap(Container container)
    {
        container.Bind<IDepenency>().To<RealThing>();
        // etc
    }
}

別のブートストラップ アセンブリを用意する必要はありませんが (このコードを BL 自体に配置できます)、これにより、ビジネス レイヤー アセンブリをコンテナーへの依存関係から解放することができます。

Bootstrap()また、(Ninject) モジュールを使用する代わりに、静的メソッドを呼び出しているだけであることにも注意してください。あなたの質問は一般的であり、アドバイスはすべてのDIフレームワークで同じであるため、フレームワークから独立した回答を維持しようとしました。ただし、もちろん、必要に応じて Ninject モジュール機能を使用できます。

于 2012-08-16T18:10:26.927 に答える
6

スコープに関して、MVC アプリケーションは、Windows サービスとは異なる CompositionRoot を持つ必要があります。できるだけ機能モジュール (アプリケーションに依存しない部分) と、MVC または WindowsService プロジェクトの CompositionRoot 内の他のすべてのバインドを直接整理することをお勧めします。

もう 1 つの非常に優れたアプローチは、最も拘束力のある懸念事項を数行で表現するのに役立つ、共通の一連の規則を定義することです。したがって、あなたのアプリは次のバインディングを持つことができます:

MVC アプリ

Bind(c => c.FromAssemblyContaining<IRepository>()
           .SelectAllClasses()
           .InheritedFrom<IRepository>()
           .Configure(b => b.InRequestScope()));

Windows サービス アプリ

Bind(c => c.FromAssemblyContaining<IRepository>()
           .SelectAllClasses()
           .InheritedFrom<IRepository>()
           .Configure(b => b.InThreadScope()));

私の見解では、機能指向の構造化と組み合わせると、従来のアプローチが最もクリーンなアプローチになります。

于 2012-08-16T17:12:03.820 に答える