Как использовать Castle Windsor с веб-формами ASP.Net? - PullRequest
33 голосов
/ 16 ноября 2008

Я пытаюсь подключить внедрение зависимостей с помощью Windsor к стандартным веб-формам asp.net. Я думаю, что я достиг этого, используя HttpModule и CustomAttribute (код показан ниже), хотя решение кажется немного неуклюжим, и мне было интересно, есть ли лучшее решение из коробки с Windsor?

Здесь показаны несколько файлов вместе

    // index.aspx.cs
    public partial class IndexPage : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            Logger.Write("page loading");
        }

        [Inject]
        public ILogger Logger { get; set; }
    }

    // WindsorHttpModule.cs
    public class WindsorHttpModule : IHttpModule
    {
        private HttpApplication _application;
        private IoCProvider _iocProvider;

        public void Init(HttpApplication context)
        {
            _application = context;
            _iocProvider = context as IoCProvider;

            if(_iocProvider == null)
            {
                throw new InvalidOperationException("Application must implement IoCProvider");
            }

            _application.PreRequestHandlerExecute += InitiateWindsor;
        }

        private void InitiateWindsor(object sender, System.EventArgs e)
        {
            Page currentPage = _application.Context.CurrentHandler as Page;
            if(currentPage != null)
            {
                InjectPropertiesOn(currentPage);
                currentPage.InitComplete += delegate { InjectUserControls(currentPage); };
            }
        }

        private void InjectUserControls(Control parent)
        {
            if(parent.Controls != null)
            {
                foreach (Control control in parent.Controls)
                {
                    if(control is UserControl)
                    {
                        InjectPropertiesOn(control);
                    }
                    InjectUserControls(control);
                }
            }
        }

        private void InjectPropertiesOn(object currentPage)
        {
            PropertyInfo[] properties = currentPage.GetType().GetProperties();
            foreach(PropertyInfo property in properties)
            {
                object[] attributes = property.GetCustomAttributes(typeof (InjectAttribute), false);
                if(attributes != null && attributes.Length > 0)
                {
                    object valueToInject = _iocProvider.Container.Resolve(property.PropertyType);
                    property.SetValue(currentPage, valueToInject, null);
                }
            }
        }
    }

    // Global.asax.cs
    public class Global : System.Web.HttpApplication, IoCProvider
    {
        private IWindsorContainer _container;

        public override void Init()
        {
            base.Init();

            InitializeIoC();
        }

        private void InitializeIoC()
        {
            _container = new WindsorContainer();
            _container.AddComponent<ILogger, Logger>();
        }

        public IWindsorContainer Container
        {
            get { return _container; }
        }
    }

    public interface IoCProvider
    {
        IWindsorContainer Container { get; }
    }

Ответы [ 5 ]

16 голосов
/ 17 ноября 2008

Я думаю, что вы в основном на правильном пути. Если вы еще этого не сделали, я бы посоветовал взглянуть на Rhino Igloo, инфраструктуру WebForms MVC, Вот хороший пост в блоге об этом и источнике здесь здесь - Айенде (автор Rhino Igloo) довольно хорошо решает проблему использования Windsor с веб-формами в этом проекте / библиотеке.

Я бы кешировал информацию об отражении, если вы собираетесь внедрить весь вложенный набор элементов управления, что может привести к снижению производительности.

Наконец, spring.net подходит к этому более ориентированным на конфигурацию способом, но, возможно, стоит взглянуть на их реализацию - вот хороший справочный пост в блоге по этому вопросу.

3 голосов
/ 10 января 2014

Вот модифицированная версия кода OP, которая (i) кэширует внедренные свойства, чтобы избежать повторных вызовов отражения, (ii) освобождает все разрешенные компоненты, (iii) инкапсулирует доступ к контейнеру, чтобы не подвергать реализации.

// global.asax.cs
public class Global : HttpApplication
{
    private static IWindsorContainer _container;

    protected void Application_Start(object sender, EventArgs e)
    {
        _container = new WindsorContainer();
        _container.Install(FromAssembly.This());
    }

    internal static object Resolve(Type type)
    {
        return _container.Resolve(type);
    }

    internal static void Release(object component)
    {
        _container.Release(component);
    }

    //...
}

// WindsorHttpModule.cs
public class WindsorHttpModule : IHttpModule
{
    // cache the properties to inject for each page
    private static readonly ConcurrentDictionary<Type, PropertyInfo[]> InjectedProperties = new ConcurrentDictionary<Type, PropertyInfo[]>();
    private HttpApplication _context;

    public void Init(HttpApplication context)
    {
        _context = context;
        _context.PreRequestHandlerExecute += InjectProperties;
        _context.EndRequest += ReleaseComponents;
    }

    private void InjectProperties(object sender, EventArgs e)
    {
        var currentPage = _context.Context.CurrentHandler as Page;
        if (currentPage != null)
        {
            InjectProperties(currentPage);
            currentPage.InitComplete += delegate { InjectUserControls(currentPage); };
        }
    }

    private void InjectUserControls(Control parent)
    {
        foreach (Control control in parent.Controls)
        {
            if (control is UserControl)
            {
                InjectProperties(control);
            }
            InjectUserControls(control);
        }
    }

    private void InjectProperties(Control control)
    {
        ResolvedComponents = new List<object>();
        var pageType = control.GetType();

        PropertyInfo[] properties;
        if (!InjectedProperties.TryGetValue(pageType, out properties))
        {
            properties = control.GetType().GetProperties()
                .Where(p => p.GetCustomAttributes(typeof(InjectAttribute), false).Length > 0)
                .ToArray();
            InjectedProperties.TryAdd(pageType, properties);
        }

        foreach (var property in properties)
        {
            var component = Global.Resolve(property.PropertyType);
            property.SetValue(control, component, null);
            ResolvedComponents.Add(component);
        }
    }

    private void ReleaseComponents(object sender, EventArgs e)
    {
        var resolvedComponents = ResolvedComponents;
        if (resolvedComponents != null)
        {
            foreach (var component in ResolvedComponents)
            {
                Global.Release(component);
            }
        }
    }

    private List<object> ResolvedComponents
    {
        get { return (List<object>)HttpContext.Current.Items["ResolvedComponents"]; }
        set { HttpContext.Current.Items["ResolvedComponents"] = value; }
    }

    public void Dispose()
    { }

}
1 голос
/ 21 февраля 2017

Единственное, чего не хватало в принятых ответах, было то, что модуль http должен быть зарегистрирован в файле web.config (в зависимости от приложения), прежде чем модуль действительно разрешит зависимости на страницах с выделенным кодом. Что вам нужно, это:

<system.webServer>
    <modules>
      <add name="ClassNameForHttpModuleHere" type="NamespaceForClass"/>
    </modules>
  </system.webServer>

Кроме того, принятые решения работали как шарм.

Ссылка на веб-сайт Microsoft для добавления модулей http: https://msdn.microsoft.com/en-us/library/ms227673.aspx

1 голос
/ 23 апреля 2013

Я недавно начал работать в компании, где существует множество устаревших приложений веб-форм, так что это выглядит очень интересным подходом и может предложить путь вперед, если мы хотим добавить DI к существующим веб-страницам, спасибо.

Одна вещь, которую я заметил, заключается в том, что метод Injection использует контейнер. Разрешить для явного разрешения компонентов, поэтому я думаю, что нам, возможно, потребуется создать контейнер. Выпуск компонентов при загрузке страницы.

Если у нас есть временные компоненты и мы этого не делаем, мы можем столкнуться с утечками памяти. Не уверен, как будут вести себя компоненты с образами жизни через веб-запрос (т. Е. Будет ли Виндзор забирать их в конце веб-запроса, даже если мы явно разрешим их), но и здесь может потребоваться безопасность.

Поэтому модуль может потребоваться расширить, чтобы отслеживать компоненты, которые он разрешает, и освобождать их, чтобы Виндзор знал, когда нужно очистить.

0 голосов
/ 09 июня 2009

Вместо того, чтобы делать это так, вы также можете использовать преобразователь типов напрямую с чем-то вроде:

ILogger Logger = ResolveType.Of<ILogger>();
...