带有IoC容器的C#ASP.NET依赖项注入
我很抱歉这么长时间,我知道有一些答案,但我搜索了很多,没有找到正确的解决方案, 所以请容忍我 我正在尝试为遗留应用程序创建一个框架,以便在ASP.NET webforms中使用DI。我可能会使用温莎城堡 作为框架 这些遗留应用程序在某些地方将部分使用MVP模式 演示者的外观如下所示:带有IoC容器的C#ASP.NET依赖项注入,c#,dependency-injection,ioc-container,C#,Dependency Injection,Ioc Container,我很抱歉这么长时间,我知道有一些答案,但我搜索了很多,没有找到正确的解决方案, 所以请容忍我 我正在尝试为遗留应用程序创建一个框架,以便在ASP.NET webforms中使用DI。我可能会使用温莎城堡 作为框架 这些遗留应用程序在某些地方将部分使用MVP模式 演示者的外观如下所示: class Presenter1 { public Presenter1(IView1 view, IRepository<User> userRepository)
class Presenter1
{
public Presenter1(IView1 view,
IRepository<User> userRepository)
{
}
}
public partial class MyPage1 : System.Web.UI.Page, IView1
{
private Presenter1 _presenter;
}
public Presenter1 Presenter { get; set; }
public MyPage1()
{
ObjectFactory.BuildUp(this);
this.Presenter.View = this;
}
在使用DI之前,我会在页面的OnInit中实例化演示者,如下所示:
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
_presenter = new Presenter1(this, new UserRepository(new SqlDataContext()));
}
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...
}
所以现在我想使用DI
首先,我必须创建一个处理程序工厂来覆盖页面的构造。
我发现这个答案很有帮助:
现在,我可以按照Mark Seeman建议使用Global.asax在我的合成根目录中轻松设置容器
(这意味着创建一个静态容器,该容器必须是线程安全且密封的,才能添加进一步的注册)
现在我可以在页面上声明构造函数注入了
public MyPage1() : base()
{
}
public MyPage1(Presenter1 presenter) : this()
{
this._presenter = presenter;
}
现在我们遇到第一个问题,我有一个循环依赖。
Presenter1依赖于IView1,但页面依赖于presenter
我知道现在有人会说,当你有循环依赖时,设计可能是不正确的。
首先,我不认为Presenter的设计是错误的,因为它将构造函数中的依赖项带到了视图中,我可以这么说
通过查看大量的MVP实现
有些人可能建议将页面更改为Presenter1成为属性的设计,然后使用属性注入
public partial class MyPage1 : System.Web.UI.Page, IView1
{
[Dependency]
public Presenter1 Presenter
{
get; set;
}
}
有些人甚至建议完全删除对presenter的依赖,然后通过一系列事件简单地连接起来,但这是错误的
这不是我想要的设计,坦率地说,我不明白为什么我需要做出这样的改变来适应它
无论如何,不管建议如何,都存在另一个问题:
当处理程序工厂收到页面请求时,只有一个类型可用(而不是视图界面):
现在,使用这种类型,您可以通过IoC及其依赖项解析页面:
container.Resolve(pageType)
class Presenter1Factory : IPresenter1Factory
{
public Presenter1Factory(Container container)
{
this._container = container;
}
public Presenter1 Create(IView1 view)
{
return new Presenter1(view, _container.Resolve<IUserRepository>,...)
}
}
这将知道有一个名为Presenter1的属性,并能够将其注入。
但是Presenter1需要IView1,但我们从未通过容器解析IView1,因此容器不会知道
为了提供具体实例,处理程序工厂刚刚创建,因为它是在容器外部创建的
因此,我们需要破解处理程序工厂并替换视图界面:
因此,处理程序工厂解析页面的位置:
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
_presenter = new Presenter1(this, new UserRepository(new SqlDataContext()));
}
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...
}
这带来了另一个问题,像Castle Windsor这样的大多数容器都不允许您重新注册此接口
指向它现在指向的实例。此外,由于容器已在Global.asax中注册,因此
此时,容器应为只读
另一种解决方案是创建一个函数,在每个web请求上重建容器,然后检查以查看
如果容器包含组件IView,则设置实例。但这似乎是浪费,与建议的用途背道而驰
另一个解决方案是创建一个名为
IPresenterFactory并将依赖项放入页面构造函数中:
public MyPage1(IPresenter1Factory factory) : this()
{
this._presenter = factory.Create(this);
}
问题是您现在需要为每个演示者创建一个工厂,然后调用容器
要解决其他依赖项,请执行以下操作:
container.Resolve(pageType)
class Presenter1Factory : IPresenter1Factory
{
public Presenter1Factory(Container container)
{
this._container = container;
}
public Presenter1 Create(IView1 view)
{
return new Presenter1(view, _container.Resolve<IUserRepository>,...)
}
}
class Presenter1工厂:iPresenter1工厂
{
公共演示者1工厂(容器)
{
这个._容器=容器;
}
公共演示者1创建(IView1视图)
{
返回新的Presenter1(视图,_container.Resolve,…)
}
}
这个设计看起来也很麻烦和复杂,有没有人有更优雅的解决方案的想法?也许我误解了你的问题,但解决方案对我来说似乎相当简单:将
IView
升级到Presenter1
上的属性:
class Presenter1
{
public Presenter1(IRepository<User> userRepository)
{
}
public IView1 View { get; set; }
}
或者,在没有属性注入的情况下,您可以按如下方式执行:
private Presenter1 _presenter;
public MyPage1()
{
this._presenter = ObjectFactory.Resolve<Presenter1>();
this._presenter.View = this;
}
私人演讲者1\u演讲者;
公共MyPage1()
{
这是。_presenter=ObjectFactory.Resolve();
this.\u presenter.View=此;
}
类和用户控件中的构造函数注入将永远不会真正起作用。您可以让它在完全信任()的情况下工作,但在部分信任的情况下会失败。因此,您必须为此调用容器
所有DI容器都是线程安全的,只要在初始化阶段之后您自己不手动添加注册,并且使用一些线程安全的容器(甚至禁止在初始化之后注册类型)。永远不需要这样做(除了大多数容器支持的未注册类型解析)。但是,使用Castle时,您需要预先注册所有具体类型,这意味着在解析它之前,它需要了解您的
Presenter1
。注册此项,更改此行为,或者移动到默认情况下允许解析具体类型的容器。也许我误解了您的问题,但解决方案对我来说相当简单:将IView
升级到Presenter1
上的属性:
class Presenter1
{
public Presenter1(IRepository<User> userRepository)
{
}
public IView1 View { get; set; }
}
或者,在没有属性注入的情况下,您可以按如下方式执行:
private Presenter1 _presenter;
public MyPage1()
{
this._presenter = ObjectFactory.Resolve<Presenter1>();
this._presenter.View = this;
}
私人演讲者1\u演讲者;
公共MyPage1()
{
这是。_presenter=ObjectFactory.Resolve();
this.\u presenter.View=此;
}
类和用户控件中的构造函数注入将永远不会真正起作用。您可以让它在完全信任()的情况下工作,但在部分信任的情况下会失败。因此,您必须为此调用容器
所有DI容器都是线程安全的,只要在初始化阶段之后您自己不手动添加注册,并且使用一些线程安全的容器(甚至禁止在初始化之后注册类型)。永远不需要这样做(除了大多数容器支持的未注册类型解析)。然而,对于Castle,您需要注册所有具体类型的upf