IoC-контейнер Castle Windsor

Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core

Последнее обновление: 31.10.2015

Еще одним распространенным 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 соответствующий пакет:

Внедрение зависимостей с Castle Windsor в ASP.NET MVC

Добавим в проект в какую-нибудь специальную папку новый класс 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>();. Затем мы можем обращаться к нему также, как в обычной программе.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850