【问题标题】:C# ASP.NET Dependency Injection with IoC Container ComplicationsC# ASP.NET 依赖注入与 IoC 容器并发症
【发布时间】:2011-11-30 10:48:34
【问题描述】:

对于篇幅太长,我深表歉意,我知道对此有一些答案,但我搜索了很多,但没有找到正确的解决方案, 所以请多多包涵。

我正在尝试为遗留应用程序创建一个框架,以在 ASP.NET 网络表单中使用 DI。我可能会使用温莎城堡 作为框架。

这些遗留应用程序将在某些地方部分使用 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 中按如下方式实例化 Presenter:

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

现在我可以按照 Mark Seeman 建议使用 Global.asax 轻松在我的组合根目录中设置容器 (这意味着虽然要创建一个静态容器,该容器必须是线程安全和密封的,不能添加进一步的注册)

现在我可以去页面上声明构造函数注入了

public MyPage1() : base()
{
}

public MyPage1(Presenter1 presenter) : this()
{
    this._presenter =  presenter;
}

现在我们遇到了第一个问题,我有一个循环依赖。 Presenter1依赖于IView1,但页面依赖于presenter。

我知道现在有些人会说,当你有循环依赖时,设计可能是不正确的。 首先,我不认为 Presenter 的设计是不正确的,因为它在构造函数中依赖于 View,我可以这样说 通过查看大量 MVP 实现。

有些人可能会建议将页面更改为 Presenter1 成为属性的设计,然后使用属性注入

public partial class MyPage1 : System.Web.UI.Page, IView1
{
    [Dependency]
    public Presenter1 Presenter
    {
        get; set;
    }
}

有些人甚至可能建议完全删除对演示者的依赖,然后简单地通过一堆事件进行连接,但这是 不是我想要的设计,坦率地说,我不明白为什么我需要做出这个改变来适应它。

不管建议如何,还有一个问题存在:

当处理程序工厂收到页面请求时,只有一个类型可用(不是视图接口):

Type pageType = page.GetType().BaseType;

现在使用这种类型,您可以通过 IoC 及其依赖项解析页面:

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

这又带来了一个问题,像Castle Windsor这样的大多数容器都不允许你重新注册这个接口 到它现在指向的实例。此外,在 Global.asax 中注册的容器也不是线程安全的 就像此时容器应该是只读的一样。

另一种解决方案是创建一个函数来在每个 web 请求上重建容器,然后检查以查看 如果容器包含组件 IView 如果未设置实例。但这似乎很浪费,并且违背了建议的用途。

另一种解决方案是创建一个特殊的工厂,称为 IPresenterFactory 并将依赖项放入页面构造函数中:

public MyPage1(IPresenter1Factory factory) : this()
{
    this._presenter = factory.Create(this);
}

问题是你现在需要为每个presenter创建一个工厂,然后调用容器 解决其他依赖关系:

class Presenter1Factory : IPresenter1Factory 
{
    public Presenter1Factory(Container container)
    {
        this._container = container;
    }
    public Presenter1 Create(IView1 view)
    {
        return new Presenter1(view, _container.Resolve<IUserRepository>,...)
    }
}

这个设计看起来也很麻烦而且过于复杂,有没有人想出一个更优雅的解决方案?

【问题讨论】:

    标签: c# dependency-injection ioc-container


    【解决方案1】:

    也许我误解了您的问题,但解决方案对我来说似乎相当简单:将IView 提升为Presenter1 上的属性:

    class Presenter1
    {
        public Presenter1(IRepository<User> userRepository)
        {
        }
    
        public IView1 View { get; set; }            
    }
    

    这样你可以像这样在视图上设置演示者:

    public Presenter1 Presenter { get; set; }
    
    public MyPage1() 
    {
        ObjectFactory.BuildUp(this);
        this.Presenter.View = this;
    }
    

    或者不用属性注入,你可以这样做:

    private Presenter1 _presenter;
    
    public MyPage1() 
    {
        this._presenter = ObjectFactory.Resolve<Presenter1>();
        this._presenter.View = this;
    }
    

    Page 类和用户控件中的构造函数注入永远不会真正起作用。你可以让它在完全信任的情况下工作 (as this article shows),但它会在部分信任下失败。所以你必须为此调用容器。

    所有 DI 容器都是线程安全的,只要您在初始化阶段之后不自己手动添加注册,并且某些容器即使是线程安全的(some containers 甚至禁止在初始化后注册类型)。永远不需要这样做(大多数容器支持的未注册类型解析除外)。但是,使用 Castle,您需要预先注册所有具体类型,这意味着它需要在您解决它之前知道您的 Presenter1。要么注册这个,改变这个行为,要么移动到一个默认允许解析具体类型的容器。

    【讨论】:

    • 嗨史蒂文,感谢您的回答,您确实正确理解了这个问题。您的解决方案有效,也许它是最好的解决方案。我试图让它与构造函数注入一起工作。通过使 View 成为一个属性,它感觉它有点破坏了封装,它现在被公开为一个可以修改的公共属性,但现在还有一个开发人员必须在每个页面上设置的附加属性。但是看起来这是唯一的方法,顺便说一句,感谢您提供的链接。
    猜你喜欢
    • 2016-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-31
    • 2016-07-19
    • 1970-01-01
    相关资源
    最近更新 更多