サーチ…


備考

依存性注入(DI)の全体のポイントは、コード結合を減らすことです。 "ハードコーディングされた依存関係の例"のようなものを新しくすることを含む相互作用があれば、どんな種類のものでも想像してください。

コードを書くことの大きな部分は、それをテストする能力です。新しい依存関係を新たにするたびに、その依存関係を制御できないため、コードをテストするのが難しくなります。

あなたはどのようにDataTime.Nowに依存するコードをテストするでしょうか?常に変更されるので、参照がありません。これは、開始点として安定したパラメータを注入するときです。あなたはそれを制御することができます、あなたは様々な値に基づいてテストを書くことができ、常に正しい結果を得ることを確認します。

したがって、インタフェースまたは抽象クラスをコンストラクタDIのパラメータとして渡すことをお勧めします。

インターフェイスは明確に定義された契約を表しています。メソッドは常にメソッドに依存していて、メソッドのシグネチャに常に頼ることができます。

DIを使用し始めると、他の側面も開きます。たとえば、ある時点でインタフェースを渡しても、実際に何らかの作業を行うには実際の実装が必要です。これは他の概念が現れる場所です。 IOC(Inversion of Control)を使用して依存関係を解決することができます。つまり、すべての契約に常に特定の実装を使用するようにコードに指示します。もちろんこれを行う方法は他にもあります。特定の実装で常に各契約をインスタンス化することができます。その点以降、コードではその部分を使用できます。

public ILogging Logging { get; set }

ある時点でそれを初期化します。

Logging = new FileLogging();

私たちのクラスが期待される契約を成し遂げている限り、これは常に有効です:

public class FileLogging : ILogging

初期化の瞬間からは、常にLoggingオブジェクトを使用します。たとえば、DatabaseLoggingを変更して使用する場合、コードを変更する必要があるのはLoggingクラスを初期化する場所だけです。

DIはテストにのみ適していますか?いいえ、DIはメンテナンス可能なコードを書くときにも重要です。懸念事項の明確化が可能です。

あなたが任意のコードを書くとき、それはテスト可能だと思う、私はテストを書くことができます、それはDateTimeを使用する代わりにDateTime値を注入するときです。

Ninject構成

IoC(Inversion of Control)コンテナをインストールした後、動作させるにはいくつか調整が必要です。この場合は、Ninjectを使用します。 App_StartフォルダにあるNinjectWebCommonファイルで、CreateKernelメソッドを次のように置き換えます。

private static IKernel CreateKernel()
    {  
        // Create the kernel with the interface to concrete bindings           
        var kernel = RegisterServices();
        try
        {
            kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
            kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
           
            return kernel;
        }
        catch
        {
            kernel.Dispose();
            throw;
        }
    }

また、RegisterServicesメソッドには、

 private static StandardKernel RegisterServices()
        {
            Container container = new Container();
            // encapsulate the interface to concrete bindings in another class or even in another layer 
            StandardKernel kernel = container.GetServices();
            return kernel;
        } 

この場合Containerと呼ばれるバインディングの新しいクラスを作成します。

public class Container
{
    public StandardKernel GetServices()
    {
        // It is good practice to create a derived class of NinjectModule to organize the binding by concerns. In this case one for the repository, one for service and one for app service bindings
        return new StandardKernel(new NinjectRepositoryModule(),
            new NinjectServiceModule(),
            new NinjectAppServiceModule());
    }
}

最後に、派生した各NinjectModuleクラスで、次のようにLoadメソッドをオーバーロードするバインディングを変更します。

public class NinjectRepositoryModule: NinjectModule
{
    public override void Load()
    {
        // When we need a generic IRepositoryBase<> to bind to a generic RepositoryBase<>
        // The typeof keyword is used because the target method is generic
        Bind(typeof (IRepositoryBase<>)).To(typeof (RepositoryBase<>));

        // When we need a IUnitOfWorkbind to UnitOfWork concrete class that is a singleton
        Bind<IUnitOfWork>().To<UnitOfWork>().InSingletonScope();
    }
}

派生したNinjectModuleの別の例:

public class NinjectServiceModule :NinjectModule
{
    public override void Load()
    {
        // When we need a IBenefitService to BenefitService concrete class           
        Bind<IBenefitService>().To<BenefitService>();

        // When we need a ICategoryService to CategoryService concrete class  
        Bind<ICategoryService>().To<CategoryService>();

        // When we need a IConditionService to ConditionService concrete class  
        Bind<IConditionService>().To<ConditionService>();           
    }
}

インタフェースの利用

サービスが必要な具体的なクラスでは、インタフェースを使用して、次のような実装ではなくサービスにアクセスします。

 public class BenefitAppService
{
    private readonly IBenefitService _service;
    public BenefitAppService(IBenefitService service)
    {
        _service = service;
    }      
   
    public void Update(Benefit benefit)
    {
        if (benefit == null) return           
        _service.Update(benefit);
        _service.Complete();
    }        
}

今あなたが具体的なクラスで何かを必要とするならば、上記のコードを妨害しないでしょう。あなたはサービスの実装を別の完全な相違に変更することができます。また、テストするのも非常に簡単です。

コンストラクタ依存性注入

Constructor Dependency Injectionには、依存関係を注入するためのコンストラクタのパラメータが必要です。したがって、新しいオブジェクトを作成するときに値を渡す必要があります。

public class Example
{
    private readonly ILogging _logging;

    public Example(ILogging logging)
    {
        this._logging = logging;
    }
}

ハードコーディングされた依存関係

public class Example
{
    private FileLogging _logging;

    public Example()
    {
        this._logging = new FileLogging();
    }
}

パラメータDI

public DateTime SomeCalculation()
{
     return DateTime.Now.AddDays(3);
}

public DateTime SomeCalculation(DateTime inputDate)
{
    return inputDate.AddDays(3);
}

最初の依存性注入

依存関係リゾルバは、緊密に結合されたクラスを避け、柔軟性を高め、テストを容易にするために使用されます。独自の依存性インジェクタを作成したり(推奨しません)、よく作成され、テストされた依存性インジェクタを使用することができます。この例では、 Ninjectを使用します。

ステップ1:依存関係リゾルバを作成します。

まずNuGetからNinjectをダウンロードしてください。 Infrastructureという名前のフォルダを作成し、 NinjectDependencyResolverという名前のクラスを追加します。

using Ninject;
using System;
using System.Collections.Generic;
using System.Web.Mvc;

public class NinjectDependencyResolver
    : IDependencyResolver
{
    private IKernel kernel;

    public NinjectDependencyResolver()
    {
        // Initialize kernel and add bindings
        kernel = new StandardKernel();
        AddBindings();
    }

    public object GetService(Type serviceType)
    {
        return kernel.TryGet(serviceType);
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        return kernel.GetAll(serviceType);
    }

    private void AddBindings()
    {
        // Bindings added here
    }
}

それは着信要求にサービスを提供するために、クラスのinsanceを必要とするときMVCフレームワークはのgetServiceGetServicesメソッドを呼び出します。

ステップ2:依存性リゾルバを登録する。

これで、カスタム依存リゾルバが作成されました。MVCフレームワークに依存リゾルバを使用させるために、カスタム依存リゾルバを登録する必要があります。依存関係リゾルバをGlobal.asax.csファイルに登録する:

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();
    DependencyResolver.SetResolver(new NinjectDependencyResolver());
    
    // .....
}

ステップ3:バインディングを追加します。

私たちが次のようなインターフェースとインプリケーションを持っているとします。

public interface ICustomCache
{
    string Info { get; }
}

public class CustomCache : ICustomCache
{
    public string Info
    {
        get
        {
            return "Hello from CustomCache.";
        }
    }
}

コントローラをCustomCacheと緊密に結合せずにコントローラでCustomCacheを使用する場合は、 ICustomCacheをCustomCacheにバインドしてNinjectを使用して注入する必要があります。 NinjectDependencyResolverの最初のもの最初、AddBindingsに次のコードを追加してCustomCacheにICustomCacheをバインド()メソッド:

private void AddBindings()
{
    // Bindings added here
    kernel.Bind<ICustomCache>().To<CustomCache>();
}

次に、以下のように注射器を準備してください:

public class HomeController : Controller
{
    private ICustomCache CustomCache { get; set; }

    public HomeController(ICustomCache customCacheParam)
    {
        if (customCacheParam == null)
            throw new ArgumentNullException(nameof(customCacheParam));

        CustomCache = customCacheParam;
    }

    public ActionResult Index()
    {
        // cacheInfo: "Hello from CustomCache."
        string cacheInfo = CustomCache.Info;

        return View();
    }
}

これは、 コストラクター注入の例であり、それは依存性注入の一形態です 。ご覧のように、私たちのホームコントローラは、CustomCacheクラス自体に依存しません。アプリケーションでICustomCacheの別の実装を使用する場合、変更する必要があるのはICustomCacheを別の実装にバインドすることだけです。これが唯一の手順です。ここで起こったことは、MVC Frameworkが、 登録された依存関係リゾルバに、 GetServiceメソッドを介してHomeControllerクラスのインスタンスを作成するよう依頼したことです。 GetServiceメソッドは、要求されたオブジェクトを作成するようにNinjectカーネルに依頼し、Ninjectカーネルはその型の型を調べ、HomeControllerのコンストラクタがICustomCacheを再キューし、 ICustomCacheのバインディングが既に追加されていることがわかります。 Ninjectはバインドされたクラスのインスタンスを作成し、それを使用してHomeControllerを作成し、それをMVCフレームワークに返します。

依存関係チェーン。

Ninjectは型を作成しようとすると、型と他の型との間の他の依存関係を検査し、いずれかのNinjectが型を作成しようとするかどうかを検査します。たとえば、CustomCacheクラスにICacheKeyProviderが必要で、ICacheKeyProviderにビニングが追加されていれば、Ninjectはそれをクラスに提供できます。

ICacheKeyProviderインターフェイスとSimpleCacheKeyProvider implentation:

public interface ICacheKeyProvider
{
    string GenerateKey(Type type);
}

public class SimpleCacheKeyProvider
    : ICacheKeyProvider
{
    public string GenerateKey(Type type)
    {
        if (type == null)
            throw new ArgumentNullException(nameof(type));

        return string.Format("{0}CacheKey", type.Name);
    }
}

変更されたCustomCacheクラス

public class CustomCache : ICustomCache
{
    private ICacheKeyProvider CacheKeyProvider { get; set; }

    public CustomCache(ICacheKeyProvider keyProviderParam)
    {
        if (keyProviderParam == null)
            throw new ArgumentNullException(nameof(keyProviderParam));

        CacheKeyProvider = keyProviderParam;
    }

    ...........
}

ICacheKeyProviderのバインディングを追加します

private void AddBindings()
{
    // Bindings added here
    kernel.Bind<ICustomCache>().To<CustomCache>();
    kernel.Bind<ICacheKeyProvider>().To<SimpleCacheKeyProvider>();
}

我々はにHomeController Ninjectに移動したときに今SimpleCacheKeyProviderのインスタンスがCustomCacheを作成するためにそれを使用してにHomeControllerを作成するためにCustomCacheインスタンスを使用しています作成されます。

Ninjectには連鎖依存性注入のような素晴らしい機能がいくつかあり、Ninjectを使いたい場合はそれらを調べるべきです。



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow