I am trying to work out a way to use dependency injection with ASP.NET Web Forms controls.

I have got lots of controls that create repositories directly, and use those to access and bind to data etc.

I am looking for a pattern where I can pass repositories to the controls externally (IoC), so my controls remain unaware of how repositories are constructed and where they come from etc.

I would prefer not to have a dependency on the IoC container from my controls, therefore I just want to be able to construct the controls with constructor or property injection.

(And just to complicate things, these controls are being constructed and placed on the page by a CMS at runtime!)

Any thoughts?

推荐答案

UPDATE 2019: With the introduction of Web Forms 4.7.2, there is now better support for DI. This invalidates the below. See: Wiring up Simple Injector in WebForms in .NET 4.7.2

You can use automatic constructor injection by replacing the default PageHandlerFactory with a custom one. This way you can use an overloaded constructor to load the dependencies. Your page might look like this:

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 () { }
}

Configuring that custom PageHandlerFactory can be done in the web.config as follows:

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

Your CustomPageHandlerFactory can look like this:

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

Downside is that this only works when running your side in Full Trust. You can read more about it here. But do note that developing ASP.NET applications in partial trust seems a lost cause.

Asp.net相关问答推荐

实体框架核心:DBCommand执行得更快,但总时间要慢10倍

502 DotNet WebApplication的网关nginx已损坏

如何处理当前文件中基本文件中的S onClick方法

Thread.CurrentPrincipal 错误地声称是匿名的

从 asp.net 中的 Web.config 获取连接字符串

从数据库中检索数据的最快方法

如何判断一个IP地址是否是私有的?

Directory.Exists 不适用于网络路径

从 JavaScript 读取 web.config

使用 Visual Studio 2012 恢复删除的文件

HTTP 错误 401.3 - 未经授权

VirtualPath 在当前应用程序根目录之外

页面在谷歌浏览器中加载两次

如何删除asp.net中的特定会话?

IIS 8.0 ASP.NET 和错误 500.19

在 Application_BeginRequest 中设置会话变量

在 ASP.NET MVC 中模拟 User.Identity

在控制器 asp.net-core 中获取当前区域性

ASP.NET 邮箱验证器正则表达式

目录与目录信息