Как использовать внедрение зависимостей в ASP.NET Web Forms - PullRequest
28 голосов
/ 26 февраля 2009

Я пытаюсь найти способ использовать внедрение зависимостей с элементами управления ASP.NET Web Forms.

У меня есть множество элементов управления, которые напрямую создают репозитории и используют их для доступа и привязки к данным и т. Д.

Я ищу схему, в которой я могу передавать репозитории внешним элементам управления (IoC), поэтому мои элементы управления не знают, как создаются репозитории и откуда они берутся и т. Д.

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

(И чтобы усложнить ситуацию, эти элементы управления создаются и размещаются на странице CMS во время выполнения!)

Есть мысли?

Ответы [ 6 ]

30 голосов
/ 03 октября 2010

Вы можете использовать автоматическое внедрение конструктора, заменив значение по умолчанию PageHandlerFactory на пользовательское. Таким образом, вы можете использовать перегруженный конструктор для загрузки зависимостей. Ваша страница может выглядеть так:

public partial class HomePage : System.Web.UI.Page
{
    private readonly IDependency dependency;

    public HomePage(IDependency dependency)
    {
        this.dependency = dependency;
    }

    // Do note this protected ctor. You need it for this to work.
    protected HomePage () { }
}

Настройка этого пользовательского PageHandlerFactory может быть выполнена в web.config следующим образом:

<?xml version="1.0"?>
<configuration>
  <system.web>
    <httpHandlers>
      <add verb="*" path="*.aspx"
        type="YourApp.CustomPageHandlerFactory, YourApp"/>
    </httpHandlers>
  </system.web>
</configuration>

Ваш CustomPageHandlerFactory может выглядеть так:

public class CustomPageHandlerFactory : PageHandlerFactory
{
    private static object GetInstance(Type type)
    {
        // TODO: Get instance using your favorite DI library.
        // for instance using the Common Service Locator:
        return Microsoft.Practices.ServiceLocation
            .ServiceLocator.Current.GetInstance(type);
    }

    public override IHttpHandler GetHandler(HttpContext cxt, 
        string type, string vPath, string path)
    {
        var page = base.GetHandler(cxt, type, vPath, path);

        if (page != null)
        {
            // Magic happens here ;-)
            InjectDependencies(page);
        }

        return page;
    }

    private static void InjectDependencies(object page)
    {
        Type pageType = page.GetType().BaseType;

        var ctor = GetInjectableCtor(pageType);

        if (ctor != null)
        {
            object[] arguments = (
                from parameter in ctor.GetParameters()
                select GetInstance(parameter.ParameterType)
                .ToArray();

            ctor.Invoke(page, arguments);
        }
    }

    private static ConstructorInfo GetInjectableCtor(
        Type type)
    {
        var overloadedPublicConstructors = (
            from constructor in type.GetConstructors()
            where constructor.GetParameters().Length > 0
            select constructor).ToArray();

        if (overloadedPublicConstructors.Length == 0)
        {
            return null;
        }

        if (overloadedPublicConstructors.Length == 1)
        {
            return overloadedPublicConstructors[0];
        }

        throw new Exception(string.Format(
            "The type {0} has multiple public " +
            "ctors and can't be initialized.", type));
    }
}

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

6 голосов
/ 18 апреля 2010

Autofac поддерживает довольно ненавязчивое внедрение зависимостей в ASP.NET WebForms. Насколько я понимаю, он просто подключается к жизненному циклу страницы ASP.NET с помощью модуля http и внедряет свойства. Единственный улов в том, что для элементов управления я не думаю, что это происходит до после события Init.

3 голосов
/ 25 июля 2018

Начиная с .NET 4.7.2 ( что нового ), разработчикам теперь легко использовать Dependency Injection в приложениях WebForms. С помощью UnityAdapter вы можете добавить его в существующее приложение WebForms за 4 простых шага. Смотрите этот блог.

3 голосов
/ 26 февраля 2009

Лучший способ - создать базовый класс для элементов управления, таких как:

public class PartialView : UserControl
{
    protected override void OnInit(System.EventArgs e)
    {
        ObjectFactory.BuildUp(this);
        base.OnInit(e);
    }
}

Это внедрит любой элемент управления, который наследуется от этого базового класса (использует Structuremap). Комбинируя это с конфигом на основе свойств, вы сможете иметь такие элементы управления, как:

public partial class AdminHeader : PartialView
{
   IMyRepository Repository{get;set;}
}

Обновление 1: Если вы не можете наследовать элементы управления, возможно, CMS имеет хук сразу после создания элементов управления, там вы можете вызвать BuildUp. Также, если CMS позволяет вам что-то перехватывать для извлечения экземпляра, вы можете использовать инжекцию на основе конструктора, но я предпочитаю BuildUp в этом конкретном сценарии, поскольку asp.net не имеет ловушки для этого.

1 голос
/ 27 февраля 2009

Вы также можете создать несколько одноэлементных экземпляров в событии application_Start global.asax и сделать их доступными как общедоступные статические свойства только для чтения.

0 голосов
/ 28 марта 2012

Это решение, которое я недавно использовал, чтобы избежать подключения к конвейеру (я нахожу, что смущает всех, кто смотрит на мой код в будущем, но да, я также вижу его преимущества):

public static class TemplateControlExtensions
{
    static readonly PerRequestObjectManager perRequestObjectManager = new PerRequestObjectManager();

    private static WIIIPDataContext GetDataContext(this TemplateControl templateControl)
    {
        var dataContext = (WIIIPDataContext) perRequestObjectManager.GetValue("DataContext");

        if (dataContext == null) 
        {
           dataContext = new WIIIPDataContext();
           perRequestObjectManager.SetValue("DataContext", dataContext);   
        }

        return dataContext;
    }

    public static IMailer GetMailer(this TemplateControl templateControl)
    {
        return (IMailer)IoC.Container.Resolve(typeof(IMailer));
    }

    public static T Query<T>(this TemplateControl templateControl, Query<T> query)
    {
        query.DataContext = GetDataContext(templateControl);
        return query.GetQuery();
    }

    public static void ExecuteCommand(this TemplateControl templateControl, Command command)
    {
        command.DataContext = GetDataContext(templateControl);
        command.Execute();
    }

    private class PerRequestObjectManager
    {
        public object GetValue(string key)
        {
            if (HttpContext.Current != null && HttpContext.Current.Items.Contains(key))
                return HttpContext.Current.Items[key];
            else
                return null;
        }

        public void SetValue(string key, object newValue)
        {
            if (HttpContext.Current != null)
                HttpContext.Current.Items[key] = newValue;
        }
    }
}

Это показывает, как вы можете довольно легко создать свой собственный менеджер продолжительности жизни, а также подключиться к контейнеру IoC, если хотите. О, и я также использую структуру запроса / команды, которая вроде не связана, но более подробно об этом можно узнать здесь:

Ограничьте свои абстракции: рефакторинг в сторону уменьшенных абстракций

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...