我对此长度表示歉意,并且我知道这里有一些答案,但是我搜索了很多并没有找到正确的解决方案,因此请耐心等待。C#ASP.NET依赖注入与IoC容器并发症
我想创建一个遗留应用程序框架在ASP.NET webforms中使用DI。我可能会使用Castle Windsor 作为框架。
这些遗留应用程序将在某些地方部分使用MVP模式。
演示者会是这个样子:
class Presenter1
{
public Presenter1(IView1 view,
IRepository<User> userRepository)
{
}
}
现在ASP.NET页面会是这个样子:
public partial class MyPage1 : System.Web.UI.Page, IView1
{
private Presenter1 _presenter;
}
使用DI我实例演示作为如下之前OnInit的页面:
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
_presenter = new Presenter1(this, new UserRepository(new SqlDataContext()));
}
所以现在我想用DI。
首先,我必须创建一个处理程序工厂来覆盖我的页面构造。 我发现这真的很不错的答案,以帮助: How to use Dependency Injection with ASP.NET Web Forms
现在,我可以很容易地建立我的容器中,我的作文根马克西曼建议使用在Global.asax (这意味着虽然创建一个静态的容器必须是线程安全的密封不能够进一步增加注册)
现在我可以去申报页面
public MyPage1() : base()
{
}
public MyPage1(Presenter1 presenter) : this()
{
this._presenter = presenter;
}
现在我们碰到的第一个问题上的构造函数注入,我有一个循环依赖。 Presenter1取决于IView1,但页面取决于演示者。
我知道有些人会说现在设计可能不正确,当你有循环依赖。 首先我不认为Presenter的设计是不正确的,因为它在构造函数中依赖于View,我可以通过查看大量的MVP实现来说这个 。
有些人可能会建议改变页到Presenter1成为属性设计,然后使用属性注入
public partial class MyPage1 : System.Web.UI.Page, IView1
{
[Dependency]
public Presenter1 Presenter
{
get; set;
}
}
有些人甚至建议取消的依赖完全主持人,然后简单地接线,通过一串事件,但这是 不是我想要的设计,并坦率地不知道为什么我需要做出这种改变来适应它。
反正不管的建议,另一个问题存在:
当处理程序工厂获取一个页面请求只有一个类型是可用的(不是视图interface):
Type pageType = page.GetType().BaseType;
现在使用这种类型的你可以通过国际奥委会和它的依赖解决页:
container.Resolve(pageType)
这就会知道有一个叫Presenter1特性,并能够注入它。 但是Presenter1需要IView1,但是我们从来没有通过容器解析过IView1,因此容器不会知道 ,因为它是在容器之外创建的,因此只提供了刚创建的处理工厂的具体实例。
所以我们需要破解我们的处理厂和更换视图界面: 那么,在处理程序工厂解决了页:
private void InjectDependencies(object page)
{
Type pageType = page.GetType().BaseType;
// hack
foreach (var intf in pageType.GetInterfaces())
{
if (typeof(IView).IsAssignableFrom(intf))
{
_container.Bind(intf,() => page);
}
}
// injectDependencies to page...
}
这带来了另一个问题,大多数容器,如温莎城堡不会让你将此接口 重新注册到它现在指向的实例。此外,在Global.asax中注册容器时,由于容器只能在此刻读取,因此它不是线程安全的,因此无法使用 。
另一种解决方案是创建一个函数来重建每个Web请求上的容器,然后检查以查看 如果容器包含组件IView(如果未设置该实例)。但这看起来很浪费,并且违背了建议的用途。
另一个解决方案是创建一个名为 IPresenterFactory特殊的工厂,并把依赖于页面的构造函数:
public MyPage1(IPresenter1Factory factory) : this()
{
this._presenter = factory.Create(this);
}
的问题是,你现在需要为每个演讲者创建一个工厂,然后作出打电话到容器 解决其他依赖:
class Presenter1Factory : IPresenter1Factory
{
public Presenter1Factory(Container container)
{
this._container = container;
}
public Presenter1 Create(IView1 view)
{
return new Presenter1(view, _container.Resolve<IUserRepository>,...)
}
}
这种设计也显得笨重和过于复杂,没有任何一个有想法,更优雅的解决方案?
嗨史蒂文,感谢您的回答,并且您确实正确地理解了这个问题。你的解决方案有效,也许它是最好的。我试图使它与构造函数注入一起工作。通过使视图属性感觉它打破了封装,它现在作为一个公共暴露出来,它可以被修改,因为它没有打算,但是现在开发人员必须在每个页面上设置一个额外的属性。但它看起来像这是唯一的方式,顺便说一句伟大的博客文章感谢链接。 – Andre