26

Я пытаюсь разработать способ использования инъекции зависимостей с элементами управления ASP.NET Web Forms.Как использовать инъекцию зависимостей с помощью веб-форм ASP.NET

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

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

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

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

Любые мысли?

ответ

30

Вы можете использовать автоматическую инъекцию конструктора, заменив по умолчанию 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)); 
    } 
} 

Даунсайд - это то, что это работает только при выполнении вашей стороны в Full Trust. Подробнее об этом можно узнать here. Но обратите внимание, что разработка приложений ASP.NET в частичном доверии seems a lost cause.

+0

Привет, Стивен, у меня есть что-то подобное в моем проекте, и это работает очень хорошо. Но сейчас я столкнулся с проблемой. Здесь описано «http://stackoverflow.com/questions/15692499/page-routing-in-asp-net-4-0-extensionless-url-versus-pagehandlerfactory-asp». Не могли бы вы взглянуть и, может быть, поделиться своим мнением? –

+0

Замок Виндзор. Nevermind, я решил это с помощью Resolving from Boostrapper на страницах. Это не круто, но эй, он работает и по-прежнему выглядит хорошо. –

+0

Я нашел эту замечательную статью http://www.codemag.com/Article/1210031 (я думаю, связанный с другим ответом SO, но теперь я не могу найти, какой), который включает в себя более примерный код, связанный с вышеупомянутым решением, и также, как интересно, показывает, как Microsoft Managed Extensibility Framework (MEF) может помочь вам решить эту и подобные проблемы инъекций зависимостей очень полезным и слегка нестандартным образом. –

3

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

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;} 
} 

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

+0

Спасибо за ответ. Перфекционистская сторона меня хотела бы, чтобы элементы управления не зависели от структуры ObjectFactory, то есть чистой инъекции зависимостей. Очевидно, это подразумевает что-то внешнее, создавая элементы управления. – Schneider

+0

Re: Обновление 1. У меня будет совать в CMS и посмотреть, смогу ли я что-нибудь найти. Я предполагаю, что одна проблема с встраиванием на основе конструктора в ASP.NET заключается в том, что элементы управления становятся «неосуществимыми» в этой точке. Если дизайнер не знает, как их создать. – Schneider

1

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

4

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

0

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

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, если захотите. О, и я также с помощью запроса/командной структуры, которая является своего рода связаны между собой, но больше на рассуждения позади, что можно найти здесь:

Limit your abstractions: Refactoring toward reduced abstractions

 Смежные вопросы

  • Нет связанных вопросов^_^