Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core
Еще одним распространенным IoC-контейнером является Castle Windsor.
Возьмем привычный пример с репозиторием:
interface IRepository { // определение методов } public class BookRepository : IDisposable, IRepository { private BookContext db = new BookContext(); public BookRepository() { } // определение методов }
И тот же контроллер:
public class HomeController : Controller { BookRepository repo; public HomeController() { repo = new BookRepository(); } public ActionResult Index() { return View(repo.List()); } }
Во-первых, добавим через NuGet соответствующий пакет:
Добавим в проект в какую-нибудь специальную папку новый класс CastleControllerFactory:
using AutofacApp.Models; using Castle.MicroKernel.Registration; using Castle.Windsor; using System; using System.Web.Mvc; using System.Web.Routing; namespace CastleApp.Util { public class CastleControllerFactory : DefaultControllerFactory { //контейнер public IWindsorContainer Container { get; protected set; } public CastleControllerFactory(IWindsorContainer container) { if (container == null) { throw new ArgumentNullException("container"); } this.Container = container; } //получение контроллера для обработки запроса protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) { if (controllerType == null) { return null; } // получаем запрошенный контроллер от Castle return Container.Resolve(controllerType) as IController; } // освобождаем контроллер public override void ReleaseController(IController controller) { var disposableController = controller as IDisposable; if (disposableController != null) { disposableController.Dispose(); } // информируем ioc-контейнер, что контроллер нам больше не нужен Container.Release(controller); } } }
Класс CastleControllerFactory представляет реализацию фабрики контроллеров. Его цель - установка контейнера Castle Windsor и
получение контроллера для обработки запроса. Также добавим новый класс ApplicationCastleInstaller
:
using CastleApp.Models; using Castle.MicroKernel.Registration; using Castle.MicroKernel.SubSystems.Configuration; using Castle.Windsor; using System.Linq; using System.Reflection; using System.Web.Mvc; namespace CastleApp.Util { public class ApplicationCastleInstaller : IWindsorInstaller { public void Install(IWindsorContainer container, IConfigurationStore store) { // регистрируем компоненты приложения container.Register(Component.For<IRepository>().ImplementedBy<BookRepository>()); // регистрируем каждый контроллер по отдельности var controllers = Assembly.GetExecutingAssembly() .GetTypes().Where(x => x.BaseType == typeof(Controller)).ToList(); foreach (var controller in controllers) { container.Register(Component.For(controller).LifestylePerWebRequest()); } } } }
Данный класс реализует интерфейс IWindsorInstaller, предоставляемый IoC-контейнером и регистрирует зависимости компонентов. В данном случае регистрируется сопоставление объектов IRepository с типом BookRepository, а также регистрируются все контроллеры, которые входят в сборку.
И чтобы внедрение зависимостей заработало, изменим файл Global.asax:
protected void Application_Start() { // создаем контейнер var container = new WindsorContainer(); // регистрируем компоненты с помощью объекта ApplicationCastleInstaller container.Install(new ApplicationCastleInstaller()); // Вызываем свою фабрику контроллеров var castleControllerFactory = new CastleControllerFactory(container); // Добавляем фабрику контроллеров для обработки запросов ControllerBuilder.Current.SetControllerFactory(castleControllerFactory); AreaRegistration.RegisterAllAreas(); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); }
Суть проведенных изменений состоит в создании контейнера и установки фабрики контроллеров для обработки запросов.
Если зависимость в виде репозитория принимает в конструкторе параметр:
public class BookRepository : IDisposable, IRepository { private BookContext db; public BookRepository(BookContext context) { db = context; } }
То мы можем установить значение для этого параметра следующим образом:
container.Register(Component.For<IRepository>().ImplementedBy<BookRepository>() .DynamicParameters((r, k) =>{ k["context"] = new BookContext(); }));
Метод DynamicParameters
принимает делегат, через который устанавливается значение параметра следующим образом: k["название_параметра"]=значение
Установка зависимостей в свойствах будет несколько сложнее. Во-первых, изменим интерфейс репозитория, чтобы он принимал контекст в виде свойства:
public interface IRepository { BookContext Context { get; set; } void Save(Book b); IEnumerable<Book> List(); Book Get(int id); } public class BookRepository : IDisposable, IRepository { private BookContext db; public BookContext Context { get { return db; } set { db = value; } } // остальные методы }
Установим значение для этого свойства:
public class ApplicationCastleInstaller : IWindsorInstaller { public void Install(IWindsorContainer container, IConfigurationStore store) { container.Register(Component.For<IRepository>().ImplementedBy<BookRepository>()); // получаем объект IRepository var repo = container.Resolve<IRepository>(); // устанавливаем у него свойство repo.Context = new BookContext(); var contollers = Assembly.GetExecutingAssembly().GetTypes() .Where(x => x.BaseType == typeof(Controller)).ToList(); foreach (var controller in contollers) { container.Register(Component.For(controller).LifestylePerWebRequest()); } } }
Чтобы установить свойство, нам надо создать нужный объект var repo = container.Resolve<IRepository>();
. Затем мы можем обращаться к нему
также, как в обычной программе.