web-dev-qa-db-ja.com

Ioc / DI-アプリケーションのエントリポイントですべてのレイヤー/アセンブリを参照する必要があるのはなぜですか?

(この質問に関連して、 EF4:遅延読み込みが有効な場合にプロキシ作成を有効にする必要があるのはなぜですか? )。

私はDIが初めてなので、我慢してください。私は、コンテナが登録済みのすべての型のインスタンス化を担当していることを理解していますが、そのためにはソリューション内のすべてのDLLとその参照への参照が必要です。

DIコンテナーを使用していなかった場合、MVC3アプリでEntityFrameworkライブラリを参照する必要はなく、DAL/Repoレイヤーを参照するビジネスレイヤーのみを参照する必要があります。

私は一日の終わりにすべてのDLLがbinフォルダに含まれていることを知っていますが、私の問題はVSの「参照の追加」を介して明示的に参照する必要があります。

112
diegohb

DIコンテナーを使用していなかった場合、MVC3アプリでEntityFrameworkライブラリを参照する必要はなく、DAL/Repoレイヤーを参照するビジネスレイヤーのみを参照します。

はい、それはまさに回避するのが難しいDIの状況です:)

密結合コードを使用すると、各ライブラリにはわずかな参照しか含まれない可能性がありますが、これらには再び他の参照があり、次のような依存関係の深いグラフが作成されます。

Deep Graph

依存関係グラフは深いため、ほとんどのライブラリは他の多くの依存関係に沿ってドラッグします。図では、Library Cに沿ってドラッグLibrary H、Library E、Library J、Library M、Library = K andLibrary N。これにより、各ライブラリを他のライブラリから独立して再利用することが難しくなります。たとえば、 in単体テスト

ただし、疎結合アプリケーションでは、すべての参照を Composition Root に移動することにより、依存関係グラフが大幅に平坦化されます

Shallow Graph

緑色で示されているように、不要な依存関係をドラッグすることなく、Library C)を再利用できるようになりました。

ただし、多くのDIコンテナでは、必要なすべてのライブラリにハード参照を追加する必要はありません。代わりに、late bindingを、コンベンションベースのアセンブリスキャン(推奨)またはXML構成の形式で使用できます。

ただし、これを行うときは、アセンブリがアプリケーションのbinフォルダーにコピーされることを忘れないでください。これは、自動的に行われないためです。個人的に、私はその余分な努力の価値があるとはめったに見つけません。

この答えのより精巧なバージョンは、私の本の this excerpt にあります Dependency Injection、Principles、Practices、Patterns

185
Mark Seemann

DIコンテナーを使用していなかった場合、MVC3アプリでEntityFrameworkライブラリを参照する必要はありません。

DIコンテナを使用する場合でも、MVC3プロジェクトにEFを参照させる必要はありませんが、(暗黙的に) Composition Root (オブジェクトを構成する起動パス)を実装することでこれを行うことを選択しますグラフ)MVC3プロジェクト内。アセンブリを使用してアーキテクチャ境界を保護することに非常に厳しい場合は、コンポジションルートまたは(MVC)プレゼンテーションのものをクラスライブラリに移動できます。

最初のオプションでは、MVC3プロジェクトがこの個別の「ブートストラップ」アセンブリを参照するようにし、ソリューション内の他のすべてのアセンブリとDIコンテナライブラリを参照します。これに伴う問題は、このブートストラッププロジェクトがMVC3プロジェクトにある型を参照できないことです(アセンブリの循環依存関係が発生するため)。これらのタイプは、ブートストラッププロジェクト(System.Web.Mvcを参照する必要がある場合があります)に移動する必要があります。または、MVC3アプリ内のコンテナー構成の一部を保持する必要があります。また、アセンブリの依存関係は推移的であるため、MVCプロジェクトは新しいブートストラップアセンブリを介して間接的に他のすべてのアセンブリを参照することに注意してください。

コンポジションルートを別のアセンブリに配置することは有効ですが、ほとんどのDI純粋主義者(私を含む)は通常、複数のエンドアプリケーション(つまり、Webアプリ+ Webサービス+ Windowsサービス)がある場合にのみコンポジションルートをクラスライブラリに移動します)同じビジネスレイヤーを使用します。単一のアプリケーションがある場合、コンポジションルートをエンドアプリケーション内に保持します。

2番目のオプションは、すべてのMVC関連クラス(ビュー、コントローラーなど)をスタートアッププロジェクトからクラスライブラリに移動することです。これにより、この新しいプレゼンテーションレイヤーアセンブリは、アプリケーションの他の部分から切断されたままになります。 Webアプリケーションプロジェクト自体は、必要なスタートアップロジックを備えた非常に薄いシェルになります。 Webアプリケーションプロジェクトは、他のすべてのアセンブリを参照するコンポジションルートになります。

プレゼンテーションロジックをクラスライブラリに抽出すると、MVCでの作業が複雑になる場合があります。コントローラーとビュー、画像、CSSファイルなどはスタートアッププロジェクトに含まれていないため、すべてを接続するのは難しくなります。これはおそらく実行可能ですが、セットアップに時間がかかります。

どちらのオプションにも欠点があります。そのため、Webプロジェクトでコンポジションルートを保持することをお勧めします。多くの開発者は、MVCアセンブリがDALアセンブリに依存することを望んでいませんが、それは実際には問題ではありません。アセンブリはdeploymentアーティファクトであることを忘れないでください。コードを複数のアセンブリに分割して、コードを個別にデプロイできるようにします。一方、アーキテクチャ層はlogicalアーティファクトです。同じアセンブリに複数のレイヤーを配置することは非常に可能です(そして一般的です)。

この場合、同じWebアプリケーションプロジェクトに(したがって同じアセンブリに)コンポジションルート(レイヤー)とプレゼンテーションレイヤーを配置することになります。また、そのアセンブリはDALを含むアセンブリを参照しますが、プレゼンテーションLayerは依然としてデータアクセスLayerを参照しません。これは大きな違いです。

もちろん、これを行うと、コンパイラがコンパイル時にこのアーキテクチャルールをチェックする機能を失いますが、これは問題になりません。実際、ほとんどのアーキテクチャルールはコンパイラによってチェックできず、常識のようなものが常にあります。チームに常識がない場合は、常にコードレビューを使用できます(すべてのチームが常にIMOを行う必要があります)。 NDepend(市販)などのツールを使用して、アーキテクチャルールの検証に役立てることもできます。 NDependをビルドプロセスに統合すると、そのようなアーキテクチャルールに違反するコードを誰かがチェックしたときに警告が表示されます。

コンポジションルートがどのように機能するかについてのより詳細な議論は、私の本の第4章で読むことができます 依存性注入、原則、実践、パターン

63
Steven

DIコンテナーを使用していなかった場合、MVC3アプリでEntityFrameworkライブラリを参照する必要はなく、DAL/Repoレイヤーを参照するビジネスレイヤーのみを参照します。

「DependencyResolver」という別のプロジェクトを作成できます。このプロジェクトでは、すべてのライブラリを参照する必要があります。

これで、UIレイヤーは、NHibernate/EFまたは参照されるCastle Windsor以外のUIに関連しない他のライブラリを必要としません。

Castle WindsorとDependencyResolverをUIレイヤーから非表示にする場合は、IoCレジストリなどを呼び出すHttpModuleを作成できます。

StructureMapの例のみがあります。

public class DependencyRegistrarModule : IHttpModule
{
    private static bool _dependenciesRegistered;
    private static readonly object Lock = new object();

    public void Init(HttpApplication context)
    {
        context.BeginRequest += (sender, args) => EnsureDependenciesRegistered();
    }

    public void Dispose() { }

    private static void EnsureDependenciesRegistered()
    {
        if (!_dependenciesRegistered)
        {
            lock (Lock)
            {
                if (!_dependenciesRegistered)
                {
                    ObjectFactory.ResetDefaults();

                    // Register all you dependencies here
                    ObjectFactory.Initialize(x => x.AddRegistry(new DependencyRegistry()));

                    new InitiailizeDefaultFactories().Configure();
                    _dependenciesRegistered = true;
                }
            }
        }
    }
}

public class InitiailizeDefaultFactories
{
    public void Configure()
    {
        StructureMapControllerFactory.GetController = type => ObjectFactory.GetInstance(type);
          ...
    }
 }

DefaultControllerFactoryはIoCコンテナーを直接使用しませんが、IoCコンテナーメソッドに委任します。

public class StructureMapControllerFactory : DefaultControllerFactory
{
    public static Func<Type, object> GetController = type =>
    {
        throw new  InvalidOperationException("The dependency callback for the StructureMapControllerFactory is not configured!");
    };

    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (controllerType == null)
        {
            return base.GetControllerInstance(requestContext, controllerType);
        }
        return GetController(controllerType) as Controller;
    }
}

GetControllerデリゲートは、StructureMapレジストリで設定されます(Windsorでは、インストーラーである必要があります)。

5
Rookian
  • 依存関係があります:オブジェクトが別のオブジェクトをインスタンス化する場合。
  • 依存関係はありません:オブジェクトが抽象化を期待する場合(コンストラクター注入、メソッド注入...)
  • アセンブリ参照(dll、webservices ..を参照)は依存関係の概念から独立しています。抽象化を解決し、コードをコンパイルできるようにするには、レイヤーが参照する必要があるためです。
0
riadh gomri