33

我正在尝试将 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; }
    }
4

5 回答 5

16

我认为你基本上走在正确的轨道上 - 如果你还没有,我建议你看看 Rhino Igloo,一个 WebForms MVC 框架,这是一篇很好的博客文章,来源在这里- Ayende(Rhino 的作者Igloo) 在这个项目/库中很好地解决了使用带有 web 表单的 Windsor 的问题。

如果你要注入整个嵌套的控件集,我会缓存反射信息,我怀疑这最终可能会有点影响性能。

最后,spring.net 以一种更加面向配置的方式来解决这个问题,但可能值得看看它们的实现——这里有一篇很好的参考博客文章

于 2008-11-17T07:17:30.517 回答
3

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

}
于 2014-01-10T07:25:15.560 回答
1

我最近开始在一家有很多遗留 webform 应用程序的公司工作,所以这看起来是一种非常有趣的方法,如果我们想将 DI 添加到现有网页,可以提供一种前进的方式,谢谢。

我注意到的一点是 Injection 方法使用 container.Resolve 来显式解析组件,因此我认为我们可能需要在页面卸载时对组件执行 container.Release。

如果我们有瞬态组件并且不这样做,那么我们可能会面临内存泄漏。不确定具有 Per Web Request 生活方式的组件会如何表现(即 Windsor 会在 Web 请求结束时拾取它们,即使我们明确解决了它们),但这里也可能需要谨慎行事。

因此,可能需要扩展模块以跟踪它解析的组件并释放它们,以便 Windsor 知道何时清理。

于 2013-04-23T14:35:55.160 回答
1

接受的答案中缺少的一件事是 http 模块需要在 web.config 文件中注册(取决于应用程序),然后模块才能真正解决代码隐藏页面上的依赖关系。你需要的是:

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

除此之外,公认的解决方案就像一种魅力。

添加http模块参考微软网站:https ://msdn.microsoft.com/en-us/library/ms227673.aspx

于 2017-02-21T09:38:11.527 回答
-2

除了这样做,您还可以直接使用类型解析器,例如:

ILogger Logger = ResolveType.Of<ILogger>();
于 2009-06-09T12:01:33.903 回答