Sponsored By Aspose - File Format APIs for .NET

Aspose are the market leader of .NET APIs for file business formats – natively work with DOCX, XLSX, PPT, PDF, MSG, MPP, images formats and many more!

Wire-Up View-Presenter Pattern Like Web Client Software Factory – Castle Windsor for Dependency Injection

Wire-Up View-Presenter Pattern Like Web Client Software Factory – Castle Windsor for Dependency Injection

by David Hayden

 

The Web Client Software Factory has a pretty cool application block called the Composite Web UI Application Block ( CWAB ) that does a number of things including using ObjectBuilder for dependency injection. One of the really cool features I like is the automatic injection of the presenter into the view class. The view class in this instance is the ASP.NET Page / Code-Behind.

 

public partial class News : Page, INewsView
{
    private NewsPresenter _presenter;

    [CreateNew]
    public NewsPresenter Presenter
    {
        get { return _presenter; }
        set { _presenter = value; }
    }

    public IList NewsHeadlines
    {
        set
        {
            NewsList.DataSource = value;
        }
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!this.IsPostBack)
        {
            this._presenter.OnViewInitialized();
        }
        this._presenter.OnViewLoaded();
    }
}

 

ObjectBuilder is responsible for injecting the Presenter into the View and knows to do this because of the [CreateNew] Attribute on the property. It injects the dependency right before the Page LifeCycle starts in Application_PreRequestHandlerExecute, and then cleans up during Application_PreRequestHandlerExecute which runs right after the Page LifeCycle

Last night before going to bed I could not resist writing my own code to wire up the View and Presenter as well as replacing ObjectBuilder with Castle Windsor for some dependency injection functionality. It turns out it is really simple although I do have some questions at the end of the post :)

First thing I needed to do was create a CreateNewAttribute that will signal the need for Dependency Injection:

 

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class CreateNewAttribute : Attribute {}

 

I then just need to add the Attribute on a View Class as mentioned above:

 

public partial class News : Page, INewsView
{
    private NewsPresenter _presenter;

    [CreateNew]
    public NewsPresenter Presenter
    {
        get { return _presenter; }
        set { _presenter = value; }
    }

    public IList NewsHeadlines
    {
        set
        {
            NewsList.DataSource = value;
        }
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!this.IsPostBack)
        {
            this._presenter.OnViewInitialized();
        }
        this._presenter.OnViewLoaded();
    }
}

 

When the Presenter is injected into the View we will also probably want the View injected into the Presenter. I am using Convention over Configuration with the Presenter Class. If the Presenter Class wants the View injected into it, it needs to declare a View Property ( no attributes necessary ).

 

public class NewsPresenter
{
    private NewsController _controller;

    public INewsView _view;
    public INewsView View
    {
        set { _view = value; }
    }

    public NewsPresenter(NewsController controller)
    {
        _controller = controller;
    }

    public void OnViewInitialized()
    {
        _view.NewsHeadlines = _controller.GetLatestNewsHeadlines();
    }

    public void OnViewLoaded()
    {
    }
}

 

Now I just need to create a custom HttpApplication Class ( or HttpModule ) that intercepts Application_PreRequestHandlerExecute and Application_PreRequestHandlerExecute to properly wire up the View and Presenter as well as use Windsor for help with other Dependency Injection needs.

During Application_PreRequestHandlerExecute I am looking for [CreateNew] Attributes on View Properties and then injecting the proper instances into it. I am focusing on injecting just the Presenter, but the code is general enough to inject any type that you have registered with Windsor.

While in there I am also looking to see if the Presenter wants the view injected into it ( looking for a View Property on the Presenter ) and doing that as well.

Note this code is raw and needs some additional work but does the trick for our simple prototype which is all I had time for last night:

 

public class Global : System.Web.HttpApplication
{
    protected virtual void Application_PreRequestHandlerExecute(object sender, EventArgs e)
    {
        HttpContext context = ((HttpApplication)sender).Context;
        this.InnerPreRequestHandlerExecute(context);
    }

    private void InnerPreRequestHandlerExecute(HttpContext context)
    {
        Page view = context.Handler as Page;

        if (view != null)
        {
            PropertyInfo[] viewProperties = view.GetType().GetProperties();

            foreach (PropertyInfo viewProperty in viewProperties)
            {
                // Look for Properties with [CreateNew] Attribute
                object[] attributes = viewProperty.GetCustomAttributes(typeof (CreateNewAttribute), true);

                if (attributes != null && attributes.Length > 0)
                {
                    // Get Presenter from Windsor
                    object presenter = IoC.Resolve(viewProperty.PropertyType);

                    // Inject Presenter Into View
                    viewProperty.SetValue(view, presenter, null);

                    // Get Presenter Properties
                    PropertyInfo[] presenterProperties = presenter.GetType().GetProperties();

                    // Look for View Property on Presenter
                    // If Found, Inject View into Presenter.
                    // Convention over Configuration
                    foreach (PropertyInfo presenterProperty in presenterProperties)
                    {
                        if (presenterProperty.Name.Equals("View"))
                            presenterProperty.SetValue(presenter, view, null);
                    }
                }
            }
        }
    }

    protected virtual void Application_PostRequestHandlerExecute(object sender, EventArgs e)
    {
        HttpContext context = ((HttpApplication)sender).Context;
        this.InnerPostRequestHandlerExecute(context);
    }

    private void InnerPostRequestHandlerExecute(HttpContext context)
    {
        // Not Sure What To Do Here if Anything.
        // Does anything need to be cleaned up????
    }
}

 

The IoC class is just a wrapper around Windsor:

 

public static class IoC
{
    private static IWindsorContainer _container =
        new WindsorContainer(new XmlInterpreter("Windsor.config"));

    public static T Resolve<T>()
    {
        return _container.Resolve<T>();
    }

    public static object Resolve(Type service)
    {
        return _container.Resolve(service);
    }
}

 

 and my Windsor Configuration looks as follows:

 

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <components>
        <component id="NewsPresenter" type="Core.Views.NewsPresenter, Core" lifestyle="transient" />
        <component id="NewsController" type="Core.NewsController, Core" lifestyle="transient" />
        <component id="NewsService" service="Core.INewsService, Core" type="Core.SqlNewsService, Core" lifestyle="singleton" />
    </components>
</configuration>

 

Windsor is coming into play during the creation of the Presenter Class. The Presenter needs to be injected with an Application Controller class and in most cases the Application Controller Class will have dependencies as well. Windsor takes care of all that. I don't believe Windsor has a BuildUp Method similar to ObjectBuilder that will inject dependencies on an existing class, so I wired the View and Presenter Myself.

Anyway, I really like the idea of having the View and Presenter Classes wired up for me automagically like done in the Composite Web UI Application Block and in the above code. Actually, I like the way MonoRail does it, but this is certainly a decent alternative when I need to work with webforms.

 

Questions and Thoughts

In WCSF, ObjectBuilder does a TearDown during Application_PostRequestHandlerExecute, but I am not sure what really needs to be cleaned up. Does anyone have any thoughts on what, if anything, should be done during Application_PostRequestHandlerExecute?

Also, does anyone know of any other / better ways to do this and any open source projects on the Internet that do this with webforms? Any enhancements to the code above are also of interest :)

Source: David Hayden

This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

6 Responses to Wire-Up View-Presenter Pattern Like Web Client Software Factory – Castle Windsor for Dependency Injection

  1. dhayden says:

    Billy,

    I am using the approach in the Web Client Software Factory where the Application Controller handles workflow.

    You may be interested in the WCSF Project because they have a Page Flow Application Block that helps with page flow. The Page Flow Application Block uses a provider model for where it gets it page flow information and they current use Windows Workflow Foundation as a provider. This means you can describe the page flow in a separate workflow document and update it without hardcoding flow in your application. It currently has a few limitations in version 1.0 and a nasty design-time bug, but is a great idea for abstracting out workflow in ASP.NET Applications.

  2. inoodle says:

    I put together a framework called PoCoRail for this sort of thing, and would be interested in some feedback. It combines an action based url approach with general MVP goodness, and handles all the automagic wireup via a custom placeholder control, so it plays nicely with standard webforms.

    http://intrepidnoodle.com/blog/show/8.aspx

  3. Great idea David! My current technique has been to use an “ASPX as View Initializer” which wires up user controls (the views) with their respective presenters. In this way, I can have multiple views on the same page without adding a lot of complexity to the containing ASPX page. Additionally, I let the ASPX page (the wirer-upper) to handle redirects. For example, a user control/view may raise an event to signal that whatever action was completed successfully. The ASPX then redirects, accordingly. In this way, the view never needs to know about application flow.

    But I love your approach to using Castle Windsor to handle all the wire-up stuff. Who then do you let handle application flow? Do you generally give that responsibility directly to the views, use a front controller, or some other framework such as Maverick.NET?

  4. dhayden says:

    @Ayende,

    Thanks for the tip. I will investigate the release policy and igloo.

    @wcpierce,

    I’ll check it out as I would enjoy learning alternatives and other solutions.

  5. wcpierce says:

    Hey David,
    I’ve dabbled with MVP + Windsor myself with some success.

    http://blogs.meetandplay.com/WPierce/archive/2006/12/24/Revisited_MVP_Framework.aspx

    My approach is slightly different and I merge the controller with the presenter. Also, my views are components in the container so they can be injected into the presenter.

    I have a single .aspx page that hosts all presenters, similar to DNN. So, the code for that page oninit looks similar to this:

    IBasketPresenter basketPresenter = IoC.Resolve();
    viewPlaceholder.Controls.Add(basketPresenter.View);
    basketPresenter.Initialize(!IsPostBack);

    Just like you, I love feedback 😉

  6. TearDown is generally a good idea to do anyway, because you will end up with resources in the presenters eventually.
    Windsor has a release policy for this.

    I also suggest that you would take a look at Igloo.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>