26

我正在尝试解决使用ASP.NET Web窗体控件的依赖注入的方法。如何在ASP.NET Web窗体中使用依赖注入

我有许多的直接创建存储库,并使用它们来访问和绑定到我要寻找一个图形数据等

在那里我可以通过仓库的外部控制(IOC)的控制,所以我的控件仍然不知道存储库是如何构造的,以及它们来自哪里等。

我不希望从控件中获得对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)); 
    } 
} 

缺点是,这只有在完全信任的情况下运行你的身边时才有效。您可以阅读更多关于它here。但请注意,开发部分信任的ASP.NET应用程序seems a lost cause

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

更新1:如果你不能拥有控制继承,也许CMS有一个钩创建的控制权后, ,在那里你可以打电话给BuildUp。同样,如果CMS允许你钩住某些东西来获取实例,你可以使用基于构造函数的注入,但是我更喜欢BuildUp,因为asp.net没有这个钩子。

+0

感谢您的答复。我的完美主义者希望控件不要依赖于ObjectFactory框架,即纯依赖注入。显然这暗示着一些外部的东西,创造了控制。 – Schneider 2009-02-26 09:07:50

+0

回复:更新1.我将在CMS中捅一捅,看看我能否找到任何东西。我想在ASP.NET中基于构造函数的注入有一个问题,那就是控件在那个时候变得“不可信”。除非设计师知道如何构建它们。 – Schneider 2009-02-26 09:09:48

1

您还可以在Application_Start global.asax事件中创建一些单例实例,并使它们可用作公共静态只读属性。

4

Autofac supports在ASP.NET WebForms中相当不显眼的依赖注入。我的理解是,它只是使用http模块挂钩到ASP.NET页面生命周期中,并执行属性注入。唯一的问题是,对于控件,我不认为这发生在之后, 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