多くの場合、アプリケーションで異なるモジュールの同じクラスインスタンスが使用されます。 これを実装する簡単な方法は、Singletonテンプレートを使用することです。
しかし、一方でこの状況を考慮してください。 このオブジェクトは最初のアクセス時に作成されるため、そのライフタイムを制御することはできません。 単体テストでは、このオブジェクトを使用する必要はありません(または不可能な場合があります)。 これを避けるために、オブジェクトを直接呼び出すのではなく、インターフェイスを介して呼び出します。 テスト用の実際のクラスインスタンスとスタブインスタンスの両方が、このインターフェイスを実装します。 そして、作成ロジックをDIコンテナに委ねます。
たとえば、サービスを使用する前。 いくつかのクラス、Killメソッドを使用したIWeaponインターフェイス、BazukaとSwordの2つの実装クラス、および武器を使用するWarriorクラスについて説明しましょう。
public interface IWeapon { void Kill(); } public class Bazuka : IWeapon { public void Kill() { Console.WriteLine("BIG BADABUM!"); } } public class Sword : IWeapon { public void Kill() { Console.WriteLine("Chuk-chuck"); } } public class Warrior { readonly IWeapon Weapon; public Warrior(IWeapon weapon) { this.Weapon = weapon; } public void Kill() { Weapon.Kill(); } }
これを使用します:
class Program { static void Main(string[] args) { Warrior warrior = new Warrior(new Bazuka()); warrior.Kill(); Console.ReadLine(); } }
行間を読みます。 私たちは戦士を作り、彼にバズーカを与え、彼は行って殺します。 コンソールで以下を取得します。
BIG BADABUM!
行にnullチェックがないことに注意してください
Weapon.Kill();
ここで何が間違っていますか? 戦士は自分が武器を持っているかどうかを知りません、そして武器の発行は独立したモジュールではなく、メインプログラムです。
DIの本質は、別のモジュールへの武器の配送を指示することです。
Ninjectを接続します。
Install-Package Ninject
武器の発行を扱うモジュールを作成します。
public class WeaponNinjectModule : NinjectModule { public override void Load() { this.Bind<IWeapon>().To<Sword>(); } }
これは文字通り「武器を要求したら剣を配る」という意味です。
「サービスロケーター」を作成し、武器を使用します。
class Program { public static IKernel AppKernel; static void Main(string[] args) { AppKernel = new StandardKernel(new WeaponNinjectModule()); var warrior = AppKernel.Get<Warrior>(); warrior.Kill(); Console.ReadLine(); } }
ご覧のとおり、新しいコンストラクトを使用して戦士オブジェクトを作成するのではなく、
AppKernel.Get<>()
を使用して作成します。 AppKernelの作成時に、武器(この場合は剣)を発行するモジュールをコンストラクターとして渡します。 これを行う方法を知っているモジュールがある場合、
AppKernel.Get
を介して取得しようとしているオブジェクトは(可能な限り)初期化されます。
アプリケーションの別の瞬間、
Warrior
オブジェクトは毎回武器を持ちませんが、検出されない場合は、サービスロケーターにアクセスして受信します。
public class OtherWarrior { private IWeapon _weapon; public IWeapon Weapon { get { if (_weapon == null) { _weapon = Program.AppKernel.Get<IWeapon>(); } return _weapon; } } public void Kill() { Weapon.Kill(); } }
実行します:
var otherWarrior = new OtherWarrior(); otherWarrior.Kill();
私たちの戦士は直接配達のために武器を受け取ります-超!
Ninjectには、もう1つの非常に素晴らしい詳細があります。 (
public property
)
public property
[Inject]
AppKernel.Get<>()
[Inject]
public property
マークされている場合、
AppKernel.Get<>()
を介してクラスを作成すると、フィールドはロケーターサービスによって初期化されます。
public class AnotherWarrior { [Inject] public IWeapon Weapon { get; set; } public void Kill() { Weapon.Kill(); } } var anotherWarrior = AppKernel.Get<AnotherWarrior>(); anotherWarrior.Kill();
団結
すべてがまったく同じです:
- 設置
Install-Package Unity
- ロケーターサービス(コンテナー)の初期化
Container = new UnityContainer();
- 登録タイプ
Container.RegisterType(typeof(IWeapon), typeof(Bazuka));
- オブジェクトの取得と使用:
var warrior = Container.Resolve<Warrior>(); warrior.Kill();
- さらに、Unityにはシングルトンクラス
(Singleton) ServiceLocator
があり、コンテナーを登録して、どこからでもサービスにアクセスできるようにします。
var serviceProvider = new UnityServiceLocator(Container); ServiceLocator.SetLocatorProvider(() => serviceProvider);
-
OtherWarrior
なOtherWarrior
は次のような武器を取得します。
public class OtherWarrior { private IWeapon _weapon; public IWeapon Weapon { get { if (_weapon == null) { _weapon = ServiceLocator.Current.GetInstance<IWeapon>(); } return _weapon; } } public void Kill() { Weapon.Kill(); } }
Autofac
そのため、実際にはすべてが起こります。
- 設置
Install-Package Autofac
- サービスロケーター(
ContainerBuilder
)のビルダーの初期化-いいえ、いいえ、これはコンテナー自体ではなく、モジュールのようなものです
var builder = new ContainerBuilder();
-
タイプの登録。 未登録クラスのインスタンスの作成はここでは実装されていないため、必要なすべてのクラスを登録する必要があります。
builder.RegisterType<Bazuka>(); builder.RegisterType<Warrior>(); builder.Register<IWeapon>(x => x.Resolve<Bazuka>());
- ロケーターサービス(コンテナー)の作成
var container = builder.Build();
- オブジェクトの取得と使用:
var warrior = container.Resolve<Warrior>(); warrior.Kill();
キャッスルウィンザー
- 設置
Install-Package Castle.Windsor
- サービスロケーターの初期化
var container = new WindsorContainer();
- タイプの登録。 Autofacに似ています。
container.Register(Component.For<IWeapon>().ImplementedBy<Bazuka>(), Component.For<Warrior>().ImplementedBy<Warrior>());
- オブジェクトの取得と使用:
var warrior = container.Resolve<Warrior>(); warrior.Kill();
小小計
実際、依存性注入の実装はそれほど多くはありませんが、まだ異なっています。
Web.config (App.config)
ファイルの初期化をサポートするものも
Web.config (App.config)
ます。 asp.net mvcのNinject拡張機能を見ると、初期化のいくつかのルールが設定されています。これは、共有オブジェクトのジェネレーターとしての、各ストリームまたはWebリクエストごとのサービスロケーターの初期化に関するものです。
エリアオブジェクト(Ninject)
Ninjectでは、クラスからオブジェクトを受け取ることを初期化するいくつかの方法を指定できます。 異なるコンテキスト(たとえば、異なるスレッド(スレッド))で作業する場合、オブジェクトは異なる方法で使用する必要があります。 したがって、アプリケーションのスケーラビリティと柔軟性がサポートされます。
面積 | バインド方法 | 説明 |
一時的な | .InTransientScope()
| クラスオブジェクトは要件ごとに作成されます(デフォルトのメソッド)。 |
ロナー | .InSingletonScope()
| クラスオブジェクトは一度作成され、再利用されます。 |
流れ | .InThreadScope()
| スレッドごとに1つのオブジェクト。 |
リクエスト | .InRequestScope()
| Webリクエストごとに1つのオブジェクトがあります |
Unityのライフタイムマネージャー
Unityは、抽象クラスLifetimeManagerの実装を使用してルールを初期化します。
次のようになります。
_container.RegisterType<DbContext, SavecashTravelContext>(new PerRequestLifetimeManager());
PerRequestLifetimeManagerはLifetimeManagerの実装です。
public class PerRequestLifetimeManager : LifetimeManager { /// <summary> /// Key to store data /// </summary> private readonly string _key = String.Format("SingletonPerRequest{0}", Guid.NewGuid()); /// <summary> /// Retrieve a value from the backing store associated with this Lifetime policy. /// </summary> /// <returns> /// the object desired, or null if no such object is currently stored. /// </returns> public override object GetValue() { if (HttpContext.Current != null && HttpContext.Current.Items.Contains(_key)) return HttpContext.Current.Items[_key]; return null; } /// <summary> /// Stores the given value into backing store for retrieval later. /// </summary> /// <param name="newValue">The object being stored.</param> public override void SetValue(object newValue) { if (HttpContext.Current != null) HttpContext.Current.Items[_key] = newValue; } /// <summary> /// Remove the given object from backing store. /// </summary> public override void RemoveValue() { if (HttpContext.Current != null && HttpContext.Current.Items.Contains(_key)) HttpContext.Current.Items.Remove(_key); } }
一番下の行。 すべてのオブジェクトは
HttpContext.Current.Items[_key]
格納され、同じコンテキスト(
HttpContext.Current
)に既に存在する場合にのみ発行されます。 それ以外の場合は、新しいオブジェクトが作成されます。 現在のコンテキスト(
HttpContext.Current
)がコード領域に存在しない場合(コンソールアプリケーションまたは別のスレッドでこのような
LifetimeManager
を使用します)、このコンテナーは機能しません。
asp.net mvcでのNinjectの使用
Ninjectをasp.net mvc環境にインストールします。 それとは別に、独自のLessonProjectプロジェクトを作成し、メソッドでHomeControllerを作成し、そこにインデックスを表示します。 (/Contollers/HomeController.cs):
public class HomeController : Controller { public ActionResult Index() { return View(); } }
そして(/Views/Home/Index.cshtml):
@{ ViewBag.Title = "LessonProject"; Layout = "~/Views/Shared/_Layout.cshtml"; } <h2>LessonProject</h2>
起動-動作します。
注:将来、このプロジェクトを後続のレッスンに移行します。
次に、このプロジェクトのNinjectモジュールとNinject.MVC3をインストールします。
Install-Package Ninject.MVC3
クラスをApp_Startフォルダー(/App_Start/NinjectWebCommon.cs)に追加します。
[assembly: WebActivator.PreApplicationStartMethod(typeof(LessonProject.App_Start.NinjectWebCommon), "Start")] [assembly: WebActivator.ApplicationShutdownMethodAttribute(typeof(LessonProject.App_Start.NinjectWebCommon), "Stop")] namespace LessonProject.App_Start { using System; using System.Web; using Microsoft.Web.Infrastructure.DynamicModuleHelper; using Ninject; using Ninject.Web.Common; public static class NinjectWebCommon { private static readonly Bootstrapper bootstrapper = new Bootstrapper(); /// <summary> /// Starts the application /// </summary> public static void Start() { DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule)); DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule)); bootstrapper.Initialize(CreateKernel); } /// <summary> /// Stops the application. /// </summary> public static void Stop() { bootstrapper.ShutDown(); } /// <summary> /// Creates the kernel that will manage your application. /// </summary> /// <returns>The created kernel.</returns> private static IKernel CreateKernel() { var kernel = new StandardKernel(); kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel); kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>(); RegisterServices(kernel); return kernel; } /// <summary> /// Load your modules or register your services here! /// </summary> /// <param name="kernel">The kernel.</param> private static void RegisterServices(IKernel kernel) { } } }
RegisterServicesでは、サービスの初期化を追加しています。 まず、遊び心のあるIWeaponを追加します。将来、他のサービスを登録するためにこのメソッドに戻ります。
public interface IWeapon { string Kill(); } … public class Bazuka : IWeapon { public string Kill() { return "BIG BADABUM!"; } } … private static void RegisterServices(IKernel kernel) { kernel.Bind<IWeapon>().To<Bazuka>(); }
コントローラーで、
[Inject]
属性を使用します。
public class HomeController : Controller { [Inject] public IWeapon weapon { get; set; } public ActionResult Index() { return View(weapon); } }
ビューを変更:
@model LessonProject.Models.IWeapon @{ ViewBag.Title = "LessonProject"; Layout = "~/Views/Shared/_Layout.cshtml"; } <h2>LessonProject</h2> <p> @Model.Kill() </p>
出力は次のとおりです。
NinjectはWebActivatorを使用します:
- OnePerRequestHttpModuleおよびNinjectHttpModuleモジュールを登録します
- StandartKernelを作成します
- サービスを初期化します。
DependencyResolver
Asp.net mvc3はDependencyResolverクラスを導入します。 このクラスは、サービスのインスタンスを提供します。 また、このクラスを介して登録済みサービス(および使用済みのDIコンテナー)を取得することもできます。
public class HomeController : Controller { private IWeapon weapon { get; set; } public HomeController() { weapon = DependencyResolver.Current.GetService<IWeapon>(); } public ActionResult Index() { return View(weapon); } }
まとめ
最新のアプリケーションでのDIコンテナの使用は、コードの強力な一貫性を取り除き、コードのあらゆる部分からサービスに簡単にアクセスするために必要です。 また、これは単体テストを書くために必要です。
すべてのソースはhttps://bitbucket.org/chernikov/lessonsにあります