Click here to Skip to main content
15,868,141 members
Articles / Web Development / ASP.NET

Understanding MVP (WCSF) Over ASP.NET Web Forms

Rate me:
Please Sign up or sign in to vote.
4.71/5 (4 votes)
13 Nov 2012CPOL8 min read 35K   471   25   5
The usage of Web Client Software Factory (WCSF) in developing enterprise web applicaitons.

Content

What is WCSF?

WCSF is a free Visual Studio extension which is useful to create composite web applications. It is mentioned that developers can create composite web applications composed of independently developed and deployed modules. These modules are dynamically brought together at run time into a common shell. The Web Client Software Factory (WCSF) provides a set of guidance for architects and developers building enterprise Web applications. The factory includes samples, reusable code and a guidance package which automates key development tasks from within Visual Studio. 

Image 1 

(From codeplex.com)

What is a composite web application?

This is a sort of web application which is composed of number of discreet and independent pieces which are integrated together within a web server environment. Composite pattern provide a flexible and scale able architecture and provide following befit to the user.

  • Higher degree of separation in business logic and application infrastructure. 
  • Code re usability across multiple solutions
  • Independent development of business logic component
  • Provides solution agility 

Understanding Shell and Modules

Web client uses composite pattern and Shell provides overall user interface throughout the solution. Shell is considered as the master page. The shell provides access to services required by modules throughout the application  

Modules contain individual functionality which integrate with user interface, communicate with shell and communicate with inter modules. Modules can be independently updated, developed, and deployed (incremental adoption of the components). 

What are the major advantages of WCSF?

Well, when considering in enterprise level, managing a web application is a tedious task. It is quite difficult to apply future changes to the existing deployment without tampering the existing application. Changes can occur in page level, class level, database level, service levels etc. Where ever the change is it is wise to pay minimum effort on deployments rather than having a fresh deployment. Followings can be described as the advantages of using WCSF over ASP.NET web forms.

  • Move page functionality in to Presenter class
  • (Changes on page level are handled on presenter and deployment is matter of replacing a dll rather than a complete deployment, Primary goal of using WCSF is to implement decoupling architecture.)       

  • Wrap common functionality in to Controller Class.
  •  Automating the testing functionality using unit testing classes. 

Disadvantage of using WCSF?

At glance too much of coding need to be done comparing coding a general ASP.NET application. This is a disadvantage when time is critical on delivery.

How to start using WCSF?

You need to download the install following extensions, 

Download and install the GAX and WCSF. Once successfully installed extension is visible on Extension Manager found on Visual Studio Tool menu as follows,   

Image 2

Creating a sample application

Let's create a sample application to understand the layers and it's usage. Let's create an empty project and add some web pages after that. Let's add a business module and several presentations (Pages). On the Visual Studio you can see the Guidance Package template on New Projects window. Select Web Client Solution (C#, Web Site) and provide name.

Image 3

Creating Product module

Let’s add the business module as Products which will contain Product.aspx, ProductView.aspx. Following illustration guide you how to add the business module. Create a module name as Products. From the appearing window you can create the unit testing project as well.

Image 4

Image 5

Adding pages to Product module

Let’s add aspx pages to Products module. Right click on the Products folder on websites section Web Client Factory > Add Page (with Presenter) (C#)

Image 6

Let's add Product.aspx. See the illustration below. Repeat the step to add ProductView.aspx.

Image 7

If you carefully inspect your Solution Explorer, following files are visible on Products business modules and each web page has an interface with presenter class.  

  • ProductPresenter.cs  
  • (Class enables you to implement Product.aspx specific business functionality)

  • IProductView.cs  
  • (Interface is used to keep the link between Product.aspx.cs and ProductPresenter.cs)

  • ProductViewPresenter.cs 
  • (Class enables you to implement ProductView.aspx specific business functionality)

  • IProductViewView.cs 
  • (Interface is to used keep the link between ProductView.aspx.cs and ProductViewPresenter.cs).

  • ProductsController.cs 
  • (Class enables you to add common functionality for presenter classes and act as a common pointer within the module)

  • IProductsController.cs 
  • (This interface links with presenter classes and control class)  

Adding controls to the pages

Just as any other web form, you can add regular ASP.Net/HTML controls on design time or run time. We have added two text boxes label and a button to the Product.aspx and three labels to ProductView.aspx

Understanding the Pattern

Following image illustrates what was done so far. Every aspx page has it's own prenseter class and presenter interface and each module has a controller class and controller interface. Because of the layered (Composite) pattern, it allows you to create individual business components independently.

Image 8 

Coding Pattern

It is very important to keep in mind when coding on aspx.cs files not to include any business logic, calculations or whatever dependency on code behind files.

Understanding Generated code

Let's look at the code behind file of Product.aspx and there will be generated code as below. Presenter class of Product.aspx is initialized here and two methods on presenter is linked to the Page_Load event. OnViewInitialized( ) and OnViewLoaded( ) methods are called respectively on Page_Load event. Presenter property is meant to store presenter object on the code behind file. 

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


[CreateNew]
public ProductPresenter Presenter
{
    get
    {
        return this._presenter;
    }
    set
    {
        if (value == null)
            throw new ArgumentNullException("value");

        this._presenter = value;
        this._presenter.View = this;
    }
}

Invoking Controller Class  and it's methods

Let's inspect the presenter class where the controller class is initialized. Following code is generated to invoke any method declared in Controller class in Presenter class. Let's say that method declared on Controller class can be accessed via _controller.methodname() within the presenter.

C#
private IProductsController _controller;

public ProductPresenter([CreateNew] IProductsController controller)
{
    //// Method on controller class invoked here
    _controller = controller;
} 

OnViewInitialized() vs OnViewLoaded()

If you look at the presenter class of Product.aspx you will find above mentioned methods. On the page load event those methods have been used. Since OnViewInitialized() method is wrapped in the (!this.IsPostback), that's ideal place to put logic like loading look up data / One time load data. Since OnViewLoaded() loads every time when the page is requested. This where to put event handling logic. 

Understanding the Code

Let's try to understanding the layered architecture and how to pass / access data between layers and some of the best practices. Since the there are many code files which are need to be coded to accomplish single task. However primary goal of the composite pattern is to keep logic isolated  and develop interdependently. Sample project mentioned here is for  technical explanatory purpose only and  does not  have any real time logic. This sample demonstrate how pass data between layers and basic usage of session. 

Product.aspx.cs

The Product.aspx.cs page is inherited by Microsoft.Practices.CompositeWeb.Web.UI.Page and IProductView. Open Product.aspx and add two Text boxes, Label and a Button. On the code behind file  add three control properties and event handler for button click. BtnSave_Click evenhandler is bound on Save button click event. Important fact is that there no any business logic on the code behind.

C#
using System;
using Microsoft.Practices.ObjectBuilder;
using System.Web.UI.WebControls;
 
namespace StockControl.Products.Views
{
    public partial class Product : Microsoft.Practices.CompositeWeb.Web.UI.Page, IProductView
    {
        private ProductPresenter _presenter; 
        public event EventHandler BtnSave_Click;
 
        public TextBox TxtProductName
        {
            get {return txtProductName;}            
            set {txtProductName = value;}
        }
 
        public TextBox TxtCost
        {
            get {return txtCost;}
            set {txtCost = value;}
        }
 
        public Label LblTitle
        {
            get{return lblTitle;} 
            set{lblTitle = value;}
        }
 
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!this.IsPostBack)
            {
                this._presenter.OnViewInitialized();
            }
            this._presenter.OnViewLoaded();
        }
 
 
        [CreateNew]
        public ProductPresenter Presenter
        {
            get {return this._presenter;}
            set {if (value == null)
                    throw new ArgumentNullException("value");
 
                this._presenter = value;
                this._presenter.View = this;}
        }
 
        protected void btnSave_Click(object sender, EventArgs e)
        { 
            if (BtnSave_Click != null)
            {
                BtnSave_Click(sender, e);
            }
        }
    }
}

IProductView.cs

Since Product.aspx.cs is inherited by IProductView interface, Properties and events on the code behind file need to be inherit from  IProductView.<code> 

C#
using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI.WebControls; 
 
namespace StockControl.Products.Views
{
    public interface IProductView 
    { 
        event EventHandler BtnSave_Click;
 
        TextBox TxtProductName { get; set; } 
        TextBox TxtCost { get; set; } 
        Label LblTitle { get; set; } 
    }
}

ProductPresenter.cs

Virtual code behind file for Products.aspx is ProductPresenter.cs. This is the ideal place to put page specific functionality. Properties declared in code behind file can be accessed via View object. Event is bound on the event OnViewLoaded() method which is called on Page load.   SetPageTitle() method is defined in the controller class and access via _controller. Page title is set on the OnViewInitialized() since the method executes only once. 

C#
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.ObjectBuilder;
using Microsoft.Practices.CompositeWeb;
using System.Web;
 
namespace StockControl.Products.Views
{
    public class ProductPresenter : Presenter<IProductView>
    { 
        private IProductsController _controller;
 
        public ProductPresenter([CreateNew] IProductsController controller)
        {
         //// Method on controller class invoked here
            _controller = controller;
        }
 
        public override void OnViewLoaded()
        {
            //// Bind event
            View.BtnSave_Click += new EventHandler(View_BtnSave_Click);
        }
 
        public override void OnViewInitialized()
        {
 
            View.LblTitle.Text = _controller.SetPageTitle();
        }
 
        private void View_BtnSave_Click(object sender, EventArgs e)
        {
          //// You can access asp.net controls on the page using View        
            HttpContext.Current.Session["ProductName"] = View.TxtProductName.Text;
            HttpContext.Current.Session["Cost"] = View.TxtCost.Text;
            HttpContext.Current.Response.Redirect("ProductView.aspx", false);
        } 
    }
}

ProductsController

Controller class is available for all the presenters within the Product module, it is good practice to declare common method which remove any redundant codes. Controller class is inherited from IProductsController interface. Let's add a common method SetPageTitle() to display module title throughout the Product module.  

C#
using System;

namespace StockControl.Products
{
    public class ProductsController : IProductsController
    {
        public ProductsController()
        {
           
        }
    
        public string SetPageTitle()
        {
            return "Product Management";
        }
    }
}

IProductsController

SetPageTitle() need to be inherited from IProdutsController. Following is the interface which was generated for the ProductController class.

C#
using System;

namespace StockControl.Products
{
    public interface IProductsController
    {
        string SetPageTitle();
    }
}

Using session on presenter/controller

Session is available on both Presenter and Controller class. So Controller is best place to populate logged user information hence all the presenter class within the product module need to access user information.   ProductsView.apx is used display data which was added on Product.aspx. There are no event this page and only labels to display data. Session can be accessed via HttpContext.Current.Session["sessionname"] snippet. 

ProductView.aspx.cs
C++
using System;
using Microsoft.Practices.ObjectBuilder;
using System.Web.UI.WebControls;
 
namespace StockControl.Products.Views
{
    public partial class ProductView : Microsoft.Practices.CompositeWeb.Web.UI.Page, IProductViewView
    {
        private ProductViewPresenter _presenter;
 
        public Label LblProductName
        {
            get
            {
                return lblProductName;
            }
 
            set
            {
                lblProductName = value;
            }
        }
        
        public Label LblProductCost
        {
            get
            {
                return lblProductCost;
            }
 
            set
            {
                lblProductCost = value;
            }
        }
       
        public Label LblTitle
        {
            get
            {
                return lblTitle;
            }
 
            set
            {
                lblTitle = value;
            }
        }
 
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!this.IsPostBack)
            {
                this._presenter.OnViewInitialized();
            }
            this._presenter.OnViewLoaded();
        }
 
        [CreateNew]
        public ProductViewPresenter Presenter
        {
            get
            {
                return this._presenter;
            }
            set
            {
                if(value == null)
                    throw new ArgumentNullException("value");
 
                this._presenter = value;
                this._presenter.View = this;
            }
        }
    }
}

Page is inherited by iProductViewView interface as Product page was. Avoid using any access modifiers on the interface class.

IProductViewView.cs

C#
using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI.WebControls;
 
namespace StockControl.Products.Views
{
    public interface IProductViewView
    {       
        Label LblProductName { get; set; }       
        Label LblProductCost { get; set; }
        Label LblTitle { get; set; }      
    }
}

ProdcutViewPresenter.cs

The ProductViewPresenter class displays the data added on Product page. SetPageTitle() is used to display the title. 

C#
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.ObjectBuilder;
using Microsoft.Practices.CompositeWeb;
using System.Web;
 
namespace StockControl.Products.Views
{
    public class ProductViewPresenter : Presenter<IProductViewView>
    {
        private IProductsController _controller;
        public ProductViewPresenter([CreateNew] IProductsController controller)
        {
            _controller = controller;
        }
 
        public override void OnViewLoaded()
        {
            string productName = (string)HttpContext.Current.Session["ProductName"];
            string productCost = (string)HttpContext.Current.Session["Cost"];
 
            //// You can access asp.net controls on the page using View
            View.LblProductName.Text = productName;
            View.LblProductCost.Text = productCost;
        }
 
        public override void OnViewInitialized()
        {
         //// Method on controller class invoked here
            View.LblTitle.Text = _controller.SetPageTitle();
        }
    }
}

Deployment

Deployment is same as the process of deploying general web form application. Just to publish and no other process required. Microsoft.Practices.CompositeWeb.dll and Microsoft.Practices.CompositeWeb.EnterpriseLibrary.dll will automatically copied to destination. 

Point of Interest

  • When removing aspx files from web site remove the related files on the modules as well
  • Target framework can be changed on project properties window.  

History

  • Nov 13, 2012: Article created.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Sri Lanka Sri Lanka
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionHow to create UNIT test Case in WCSF using Rhino Pin
SUNDAR_REDNUS7-Mar-16 2:18
SUNDAR_REDNUS7-Mar-16 2:18 
Question[CreateNew] Pin
abdelfattah_radjab28-Dec-14 6:30
abdelfattah_radjab28-Dec-14 6:30 
AnswerRe: [CreateNew] Pin
Chamila Nishantha28-Dec-14 15:31
Chamila Nishantha28-Dec-14 15:31 
[CreateNew] attribute added automatically when adding the page. You don't need to worry about it.

QuestionDependencies not added automatically Pin
netfed23-Nov-12 19:25
netfed23-Nov-12 19:25 
AnswerRe: Dependencies not added automatically Pin
Chamila Nishantha25-Nov-12 16:59
Chamila Nishantha25-Nov-12 16:59 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.