Build Composite Web Clients
An Introduction to the Web Client Software Factory
October 30, 2009
asp:Feature
LANGUAGES: C#
ASP.NETVERSIONS: 2.0
Build Composite Web Clients
An Introduction to the Web Client Software Factory
By Matt Dinovo
There has been a marked increase in the number of offerings from Microsoft s patterns & practices team in recent years. These offerings were designed to give .NET developers foundational code units that leverage well-established industry patterns to solve common tasks. These blocks of code come with thorough testing and the energy and dedication of the .NET community behind them.
In the past, offerings from the patterns & practices team included: Enterprise Library, the Composite UI Application Block (CAB) for Windows applications, and the Smart Client Software Factory. In January 2007, the Microsoft patterns & practices team released version 1.0 of the Web Client Software Factory (WCSF). WCSF was updated in June 2007 to take advantage of Enterprise Library 3.0 and enhancements in Windows Server 2008.
The release of this software factory is noteworthy because it brings two major technology patterns to the Web world: the Model-View-Presenter pattern for composite user interfaces, and state machine-driven (workflow) page flow. In this article, we ll explore these technologies, as well as how they can facilitate the development of Web applications. In addition, we ll look at the software factory guidance automation package itself, and how a developer interacts with it to produce code.
What Is a Guidance Automation Package?
As part of its Software Factories Initiative, Microsoft has put forth a way to bundle source code, help files, and more as predefined recipes for implementing certain patterns or standards. As such, all the software factories coming from the patterns & practices team are deployed as Guidance Automation Packages (at the time of this writing, there are four software factories: Smart Client, Mobile Client, Web Service, and Web Client).
These packages integrate into Visual Studio 2005 and allow recipe steps to be implemented at several points during development, such as when you create a new project or when you add a new item to an existing project. The automation scripts handle the wiring together of projects and will make necessary edits to configuration files, etc. This is a great way to rapidly put together an application that adheres to one of these patterns.
The examples shown in this article will demonstrate how the guidance automation package is used to implement a Web application; the integration with Visual Studio will be illustrated, as well. Visithttp://msdn2.microsoft.com/en-us/teamsystem/aa718948.aspx for more information about Guidance Automation Packages.
Getting Started
There are several prerequisites necessary to start using WCSF. These are enumerated on the WCSF site, so I won t go into too much detail here. But, assuming you ve installed the Guidance Automation extensions and toolkit, and .NET 3.0, you ll see a new project type available under the New Project dialog box in Visual Studio. This project type launches a wizard that will ask you several questions about the organization of your application. The Create Solution wizard dialog box is where you choose the root namespace name, as well as the directory for all required application block assemblies. These are foundational assemblies that are common to all the software factories that have their root in Enterprise Library 3.1. Finally, you can opt to show documentation when the recipe completes. For those new to software factories and how they work, I highly recommend you have this checked whenever the option presents itself.
Click Finish after making your selections the guidance package creates a solution containing a Web site that holds the actual user interface elements and a foundational module named Shell was created to hold the logic for the application. Note that the main content area contains the additional documentation pertinent to this step of the recipe. Each of the guidance activities will have a help page (similar to this one) that explains what has just been completed and what the probable next steps are. Now you can start to add views or modules to the solution to implement your Web site.
What Are Modules, Views, and Presenters?
To understand how to work with sites built using WCSF, one must understand the concepts of modules, views, and presenters. The Model-View-Presenter pattern is an architectural pattern where the view (in this case, an ASP.NET page) manages only the user interface and forwards all events to a presenter. The presenter contains the logic to respond to these events and drives the state of the view based on changes in the application state. So, to sum up, there is absolutely zero business logic in the ASPX pages themselves; rather, all pertinent data is passed back to the corresponding presenter for processing, then any requisite changes to the view are driven by the presenter.
Modules come in two flavors: business and foundational. Business modules are analogous to a use case (though it could contain multiple use cases). All of the presenters, views, interfaces, and possibly page flow (more on this later) for a given use case are encapsulated within a business module. A foundational module, on the other hand, is one that provides such services as logging or instrumentation to other modules in the system. A foundational module does not contain any user interface classes, nor does it represent a use case; rather, it spans all the use cases.
Now that we have a general understanding of the terms involved, we can begin to add our first business module to the application. Through the use of the guidance package add-in for Visual Studio, you can right-click on the Modules folder and select Web Client Factory | Add Business Module. As when the project is created, you ll see a wizard dialog box that will prompt you for the target Web site and some additional options (whether or not you wish to have unit tests generated, etc.).
When this wizard is completed, a new project will be created under the Modules folder. In addition, a folder will be created in the Web project corresponding to this module. This folder contains the actual ASPX pages; the presenter and interface exist in the new project. The guidance package took care of updating all the references and deployment paths so that everything will compile and run. At this point, if you were to view the Web site, you ll see an additional menu item in the left navigation area corresponding to the newly created module. We now can add a second view to this module by right-clicking the Modules folder in the Web project and selecting Web Client Factory | Add View (with presenter). This will add a new ASPX page to the Web site and the corresponding presenter to the business module.
To demonstrate how a view interacts with its presenter, let s create a simple scenario where a query string needs to be generated and passed from the default view to a second one. The flow is that the page initiates the event, which invokes a method on the presenter, which then performs some logic and then calls a method on the view to actually redirect the page. To begin, we need to create a method, ChangeView, in the presenter to be invoked by the view. Second, we need to add a method to the interface for the view to act as the recipient of the ChangeView call (see the presenter and interface changes in the business module below). Then we need to add the instance of this method to the view itself; it will be a simple Response.Redirect to the new view, but with the added query string provided by the presenter. Finally, we ll add a button to the view to kick everything off and wire the event to call the newly created method in the presenter.
namespace WCSFSample.SampleModule1.Views
{
public class DefaultViewPresenter : Presenter
{
public void ChangeView()
{
int randomNumber = new Random().Next();
this.View.RedirectToView1(randomNumber.ToString());
}
}
}
namespace WCSFSample.SampleModule1.Views
{
public interface IDefaultView
{
void RedirectToView1(string query);
}
}
Presenter and interface changes in the business module.
public partial class SampleModule1_Default :
System.Web.UI.Page, IDefaultView
{
private DefaultViewPresenter _presenter;
protected void Page_Load(object sender, EventArgs e)
{
if (!this.IsPostBack)
{
this._presenter.OnViewInitialized();
}
this._presenter.OnViewLoaded();
}
[CreateNew]
public DefaultViewPresenter Presenter
{
set
{
this._presenter = value;
this._presenter.View = this;
}
}
protected void btnChangeView_Click(object sender,
EventArgs e)
{
_presenter.ChangeView();
}
#region IDefaultView Members
public void RedirectToView1(string query)
{
Response.Redirect("View1.aspx?q=" + query, true);
}
#endregion
}
Call the new method in the presenter.
The result is an illustrative, albeit simplistic, example of the interaction between view and presenter. As you can tell from the example, certain functions can only happen within the view. For example, there is no sense of HttpContext at the business module, so the redirect happens at the view layer. While we could have simply included a reference System.Web at the module level and retrieved the current HttpContext, it breaks the spirit of the pattern. The idea of the business module is that it is implementation-agnostic from a user-interface perspective. In the strictest sense, you should be able to use the same business module in a Web client as well as a smart client implementation. Although this is not practical in reality there are different foundational blocks for Web clients and smart clients that are referenced by the module, so using the same module for both would not be possible the idea is to develop your module as if this was the case.
Obviously, this example is incredibly simple. However, what should be gleaned from this example is how modular everything is. Different business modules easily can be developed in parallel without impacting the Web site itself. The site is simply a container that rubber-bands together the logic in each of the specific modules. One can see how a large intranet site, for example, could have business modules for human resources, payroll, information technology, etc., that could all be developed internally by each department without any knowledge of the function, or even the existence, of each other.
Integration with Windows Workflow
One shortcoming of the previous example was that the destination view was hard-coded in the page code itself. What if we wanted to have navigation sequence be dictated by the actions of the user? Or wanted to change this sequence without having to change the views themselves? These scenarios (and more) can be accommodated when we marry WCSF with Windows Workflow. This is accomplished using what WCSF refers to as a page flow, which is simply a set of individual pages orchestrated by a workflow description. In this case, we ll use a Windows Workflow state machine to describe the individual states (which equate to pages in the site), then use the business modules to define the state of the user as they traverse the site. The workflow engine will then determine the page associated with that state and present that view to the user.
To get started, let s create a new WCSF project just as we did in the previous example. To this project we ll add a Page Flow Project named PageFlowProject1 using the same menu as when we added a business module. Once the wizard completes, you ll have a Windows Workflow project with an empty designer named PageFlow1. At this point, there s a bit of configuration that must happen before we can start using workflow with our site. Once the project is set up, we must prepare the database for page flow.
Windows Workflow uses a SQL Server database to persist state, as well as track various events within the workflow (such as transitions from one state to another). In addition, WCSF uses a specific structure to manage page flow state. The scripts to create the workflow objects can be found at %WINDOWS%Microsoft.NETFrameworkv3.0Windows Workflow FoundationSQLEN. The WCSF script is installed as part of the WCSF source install package. In the downloadable code for this article, a starter database has been included with all of the appropriate structures (see end of article for download details).
Finally, the last configuration step is to enable the Web site for page flow. To do this, we ll first add Microsoft.Practices.PageFlow, Microsoft.Practices.PageFlow.WorkflowFoundation, and Microsoft.Practices.PageFlow.Storage.EnterpriseLibrary references to the Web site. Then we ll add additional configuration sections and system.web sections to the site s web.config file to enable connectivity to the workflow database, and add the HttpModule that will manage the page rendering. Additional details on enabling the site for page flow can be found in the online help installed with WCSF.
After all the configuration, we can now begin to create the site. For this example, we re going to create a simple page that asks a question; depending on the answer to that question, the user will be directed to one of two views. To begin, create a new business module named QuestionModule (as in the previous example) and add a reference to PageFlowProject1, as well as the three PageFlow assemblies referenced by the Web site. As part of the creation of this module, a new folder in the Web site with the same name was also created. Using the WCSF wizard, we ll add to the Web site three views: AskQuestion, CorrectAnswer, and IncorrectAnswer. To do this, right-click on the QuestionModule folder in the Web site and select Web Client Factory | Add View (with presenter). On the AskQuestion.aspx page, add a simple question with a radio button answer.
Now we can begin to model the workflow. For this example state machine, drag three PageState objects from the toolbox (you ll have to add the Microsoft.Practices.PageFlow.WorkflowFoundation DLL to the toolbox) and name them Home, CorrectAnswer, and IncorrectAnswer. Each of these states is then associated to an ASPX page in the Web site. Add an EventDriven activity to the Home state and rename it EvaluateQuestion. Without going too deep into the weeds, every workflow has a collection named UserDataStore to hold and act upon data. Objects external to the workflow can add data to this collection and the workflow can react to it. In this example, the branching logic looks for a key in the UserDataStore named Answer and transitions to the CorrectAnswer state if the answer is right; if not, the flow is transitioned to the IncorrectAnswer state.
The last step in integrating page flow with WCSF is to activate and interact with the state machine. To do this, we ll use the QuestionModuleController class that was automatically created for us when we used the guidance package. This class is empty by default. However, all presenters for a given module can have an instance of this class injected into it as part of the initialization process. It is this class that will start the workflow, push the answer into it, and complete the workflow at the end of all the transitions. Each presenter will then have access to the controller. In this example, the Default view starts the workflow, then calls RedirectToCurrentPage which redirects the user to whatever state they are in in the workflow (which, in this case, redirects the user to AskQuestion.aspx). Once the user makes a selection and submits an answer, the AskQuestion presenter will then push the answer into the UserDataStore collection, then navigate to the EvaluateQuestion transition. It is important to note that nowhere does the code actually call out the destination page itself only the name of the event handler activity. Redirection to the actual page is the responsibility of the PageFlow HttpModule. The workflow then tests the answer and transitions to the appropriate state and ends the workflow.
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.CompositeWeb;
using Microsoft.Practices.PageFlow;
using WCSFPageFlowSample.PageFlowProject1;
namespace WCSFPageFlowSample.QuestionModule
{
public class QuestionModuleController
{
private IPageFlow _questionPageFlow;
private IPageFlowProvider _pageFlowProvider;
public QuestionModuleController(
[ProviderDependency(typeof(PageFlowDirectory))]
IPageFlowProvider pageFlowProvider)
{
_pageFlowProvider = pageFlowProvider;
}
protected IPageFlow QuestionPageFlow
{
get
{
if (_questionPageFlow == null)
{
_questionPageFlow =
_pageFlowProvider.GetPageFlow(
typeof(PageFlow1));
}
return _questionPageFlow;
}
}
public virtual void StartPageFlow()
{
QuestionPageFlow.Start();
QuestionPageFlow.RedirectToCurrentPage();
}
public virtual void AnswerQuestion(string answer)
{
QuestionPageFlow.UserData["Answer"] = answer;
QuestionPageFlow.Navigate("EvaluateQuestion");
}
public virtual void FinishPageFlow()
{
QuestionPageFlow.Complete();
}
}
}
The QuestionModuleController class.
This example highlights the real power of associating a page flow with the site. Users can be lead through a site without being able to circumvent the predisposed path. If a user were to start the workflow in this example, then manually tried to go to IncorrectAnswer.aspx without actually answering the question, the user would automatically be returned to the question page because that is the current state of the workflow. The prototypical example for this feature are shopping sites where there is a well-defined process for checking out (filling out shipping information, entering payment details, confirming the order, submitting the order, etc.).
The other benefit from using page flow is that navigation is handled externally. The pages associated with states can be changed without changing the Web site (or accompanying business modules). Simply by restructuring the workflow and deploying the changed assembly, the entire flow of a site can be completely changed without ever disturbing the site itself.
Conclusion
This brief introduction to the Web Client Software Factory illustrates the power and flexibility of creating composite Web sites using atomic business modules that can be developed without knowledge of, or impact to, other modules in the system. In addition, the use of Windows Workflow to implement page flow allows a developer to easily drive and change a user s path through a site without having to touch the ASP.NET code at all. For additional examples and information, or to download the bits, visit the WCSF community site at http://www.codeplex.com/websf.
The files referenced in this article is available fordownload.
Matt Dinovo is a Senior Solution Developer at Avanade Inc., a Seattle-based integrator for Microsoft technology that is a joint venture between Accenture Ltd. and Microsoft. You can reach him on his blog at mailto:mattdinovo.spaces.live.com.
Read more about:
MicrosoftAbout the Author
You May Also Like