使用StructureMap在模型 - 视图 - 展示器模式中进行Presenter注入

时间:2009-02-05 05:02:28

标签: c# dependency-injection structuremap mvp

我已经实现了我自己的模型视图演示者模式的副本(在Web客户端软件工厂的静脉中),所以我可以利用自己的DI框架,而不是绑定到WCSF的ObjectBuilder,我遇到了很多问题。我想出了几个方法来做到这一点,但没有一个特别让我开心。我想知道是否有其他人有其他想法。

解决方案#1a

使用HttpModule拦截context.PreRequestHandlerExecute以调用ObjectFactory.BuildUp(HttpContext.Current.Handler)

public partial class _Default : Page, IEmployeeView
{
    private EmployeePresenter _presenter;

    private EmployeePresenter Presenter
    {
        set
        {
            _presenter = value;
            _presenter.View = this;
        }
    }
}

解决方案#1b

在页面加载中调用buildup而不是使用HttpModule

public partial class _Default : Page, IEmployeeView
{
    private EmployeePresenter _presenter;

    private EmployeePresenter Presenter
    {
        set
        {
            _presenter = value;
            _presenter.View = this;
        }
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        ObjectFactory.BuildUp(this);
    }
}

解决方案#1c

通过Property访问presenter,如果需要,允许Getter到BuildUp。

public partial class _Default : Page, IEmployeeView
{
    private EmployeePresenter _presenter;

    public EmployeePresenter Presenter
    {
        get
        {
            if (_presenter == null)
            {
                ObjectFactory.BuildUp(this);
            }

            return _presenter;
        }
        set
        {
            _presenter = value;
            _presenter.View = this;
        }
    }
}

解决方案#2

public partial class _Default : Page, IEmployeeView
{
    private EmployeePresenter _presenter;

    private EmployeePresenter Presenter
    {
        get
        {
            if (_presenter == null)
            {
                _presenter = ObjectFactory.GetInstance<EmployeePresenter>();
                _presenter.View = this;
            }

            return _presenter;
        }
    }
}

解决方案#2b

public partial class _Default : Page, IEmployeeView
{
    private EmployeePresenter _presenter;

    private EmployeePresenter Presenter
    {
        get
        {
            if (_presenter == null)
            {
                Presenter = ObjectFactory.GetInstance<EmployeePresenter>();
            }

            return _presenter;
        }
        set
        {
            _presenter = value;
            _presenter.View = this;
        }
    }
}

修改:添加了解决方案1c,2b

4 个答案:

答案 0 :(得分:7)

我使用解决方案#1b,并为所有页面创建layer supertype,以便更多地干掉演示者初始化。像这样:

网页代码:

public partial class _Default : AbstractPage, IEmployeeView
{
    private EmployeePresenter presenter;

    private EmployeePresenter Presenter
    {
        set
        {
            presenter = value;
            presenter.View = this;
        }
    }
    protected override void Do_Load(object sender, EventArgs args)
    {
        //do "on load" stuff 
    }

}

摘要页码:

public abstract class AbstractPage : Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        ObjectFactory.BuildUp(this);
        this.Do_Load(sender,e); 
        //template method, to enable subclasses to mimic "Page_load" event

    }
    //Default Implementation (do nothing)
    protected virtual void Do_Load(object sender, EventArgs e){}
}

使用此解决方案,您只能在一个类中进行演示者初始化(由ObjectFactory创建),如果您以后需要修改它,则可以轻松完成。

修改

Do_Load应抽象还是虚拟

Template Method最初声明该方法应该是Abstract,以强制子类实现它,遵守超类契约。 (参见维基百科“垄断”的例子&lt;“游戏”)。

另一方面,在这种特殊情况下,我们不想强制用户类重新定义我们的方法,但给它机会这样做。如果你声明它是抽象的,许多类将被迫重新定义方法只是为了让它为空(这显然是代码味道)。因此,我们提供合理的默认值(不执行任何操作)并使方法成为虚拟。

答案 1 :(得分:1)

我一直在使用基页类:

protected override void OnInit(EventArgs e)
    {
        StructureMap.ObjectFactory.BuildUp(this);
        base.OnInit(e);
    }

基类方法也适用于用户控件,仅此一项就让我不能使用模块(不希望有两种方法来设置它)。 对于页面

public partial class Employee : View, IEmployeeView
{
    public ViewPresenter Presenter { get; set; }
    private void Page_Load(object sender, EventArgs e){}
}

我通过构造函数注入视图。要避免在structuremap配置上出现循环引用问题,只需使用此辅助方法:

static T GetView<T>()
{
    return (T) HttpContext.Current.Handler;
}

在structuremap config上,为演示者和视图注入使用约定。

答案 2 :(得分:1)

我也建立了自己的MVP框架。我发现对我来说最好的方法是使用泛型和基页类。通过在泛型类定义中指定Presenter类型,我会错过每个提案所需的大部分代码。

然而,有些事情我不喜欢这样做。类定义最终可能看起来相当复杂,并且对于新手来说并不容易阅读。我还没有完全找到在基页中使用事件模型的好方法。

抱歉,我这里没有您的代码,但如果您愿意,我可以为您发布一些代码。我还有一个旧版本的代码发布在www.codeplex.com/aspnetmvp,如果你想看看它是如何工作的。

答案 3 :(得分:0)

感谢大家的宝贵意见。你的答案给了我很有价值的想法,在我的最终解决方案中结合在一起,这就是我想出的:

public abstract class ViewBasePage<TPresenter, TView> :
    Page where TPresenter : Presenter<TView>
{
    protected TPresenter _presenter;

    public TPresenter Presenter
    {
        set
        {
            _presenter = value;
            _presenter.View = GetView();
        }
    }

    /// <summary>
    /// Gets the view. This will get the page during the ASP.NET
    /// life cycle where the physical page inherits the view
    /// </summary>
    /// <returns></returns>    
    private static TView GetView()
    {
        return (TView) HttpContext.Current.Handler;
    }

    protected override void OnPreInit(EventArgs e)
    {
        ObjectFactory.BuildUp(this);
        base.OnPreInit(e);
    }
}

由我的原始页面继承:

public partial class _Default : 
    ViewBasePage<EmployeePresenter, IEmployeeView>, IEmployeeView
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!Page.IsPostBack)
        {
            _presenter.OnViewInitialized();
        }

        _presenter.OnViewLoaded();
        Page.DataBind();
    }

    #region Implementation of IEmployeeView

    ...

    #endregion
}