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.
(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,

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.

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.


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#)

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

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.
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.
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.
private IProductsController _controller;
public ProductPresenter([CreateNew] IProductsController controller)
{
_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.
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.
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.
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)
{
_controller = controller;
}
public override void OnViewLoaded()
{
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)
{
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.
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.
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
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
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.
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"];
View.LblProductName.Text = productName;
View.LblProductCost.Text = productCost;
}
public override void OnViewInitialized()
{
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.