Using the WCSF with existing web site projects

 

Note: This is a parallel post to Using the WCSF with existing web application projects . It will cover the same topics, with (almost) the same introduction and purpose for the other type of web project in Visual Studio 2005.

We have what I would call a great offering in the Web Client Software Factory. However, all of the guidance to date has been around using the WCSF in a green-field scenario, where you can start fresh. I know that in the real world, this is not often the case. This point was definitely driven home for me over the last three days. On Monday and Tuesday, we had a WCSF Workshop (see Mariano's post for some info on how it went), In talking to customers during and after the workshop, there were numerous requests on how to incorporate the WCSF into existing web site projects, and then how to convert the site from the current architecture to the WCSF architecture. To do this, we need to do three things:

  • Enable the guidance package
  • Incorporate the Composite Web UI Application Block into the web site project
  • Move associated web pages into modules
  • Refactor existing business logic into foundational modules and expose them as services, which are consumed by the existing pages
  • Update the web pages to use the View Presenter pattern.

Today, I'm going to tackle the first two steps on a really simple web site project. Further posts will talk about the re-architecture of an existing site (I just need to create a reasonably complex, not well structured, web site first).

Before You Begin

I am starting with a basic web site project, with one web page. You could start with a copy of your existing web site, which you should back up, just in case. Either way, the instructions will be the same.

Enabling the Guidance Package

The first step to enabling the guidance package is to modify the SLN file. This requires opening the SLN in your favorite text editor and adding the following section inside the Global section:

GlobalSection(ExtensibilityGlobals) = postSolution

  RootNamespace = MyExistingWebSiteProject

  IsWCSFSolution = True

EndGlobalSection

Save the SLN file. 

Now, you can open your solution in Visual Studio. Go to Tools-> Guidance Package Manager, and enable the "Web Client Development" Guidance Package. Nothing will be available yet, but we will enable all of the recipes before we are done.

Adding the Composite Web UI Application Block to your Web Site

Now, we can enable CWAB on your site, so dependency injection will work, and the new modules and pages will work without throwing exceptions.

Copy Files

This is simplest if you use the GP and create a new "Web Client Solution" and can copy some files and folders.

When you create a new Web Client Application solution, a Library folder is created for you with all the DLLs you need. You will need to copy the Library folder from an existing WCSF project to the folder that contains your SLN file. This gets CWAB, all of the configuration, security, and exception shielding that you need.

Then, in the folder that your web site is in, copy over the Modules folder from your sample solution, so we have a Shell module to work with.

Also, copy a file called vwd.webinfo from the web site to your web site.

Your solution folder on the file system should look something like this:

  • MyLegacyWebSite
    • Library
      • Microsoft.Practices.CompositeWeb.dll
      • Microsoft.Practices.CompositeWeb.EnterpriseLibrary.dll
      • Microsoft.Practices.EnterpriseLibrary.Common.dll
      • Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.dll
      • Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging.dll
      • Microsoft.Practices.EnterpriseLibrary.Logging.dll
      • Microsoft.Practices.EnterpriseLibrary.Security.dll
      • Microsoft.Practices.ObjectBuilder.dll
      • Microsoft.Practices.ObjectBuilder.WCSFExtensions.dll
    • Modules
      • Shell
      • ...
    • MyLegacyWebSite
      • … all you existing files, etc
    • MyLegacyWebSite.sln

Edit the Web.Config

Next, open up the Web.Config for the web application.

At the top of the file, right under the opening <configuration> tag, paste the following huge snippet:

<configSections>
<sectionGroup name="compositeWeb">
<section name="modules" type="Microsoft.Practices.CompositeWeb.Configuration.ModulesConfigurationSection, Microsoft.Practices.CompositeWeb"/>
<section name="authorization" type="Microsoft.Practices.CompositeWeb.Configuration.AuthorizationConfigurationSection, Microsoft.Practices.CompositeWeb"/>
</sectionGroup>
<section name="securityConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Security.Configuration.SecuritySettings, Microsoft.Practices.EnterpriseLibrary.Security, Version=2.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
<section name="loggingConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.LoggingSettings, Microsoft.Practices.EnterpriseLibrary.Logging, Version=2.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
<section name="exceptionHandling" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Configuration.ExceptionHandlingSettings, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=2.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
</configSections>

<compositeWeb>
<modules>
<module name="Shell" assemblyName="Shell" virtualPath="~/" />
</modules>
</compositeWeb>
<securityConfiguration defaultAuthorizationInstance="RuleProvider" defaultSecurityCacheInstance="">
<authorizationProviders>
<add type="Microsoft.Practices.EnterpriseLibrary.Security.AuthorizationRuleProvider, Microsoft.Practices.EnterpriseLibrary.Security, Version=2.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" name="RuleProvider">
<rules>
<!-- Add your own rules here:
e.g.:
<add expression="R:Customer" name="AllowViewAccountsSummary"/>
-->
</rules>
</add>
</authorizationProviders>
</securityConfiguration>
<loggingConfiguration name="Logging Application Block" tracingEnabled="true" defaultCategory="General" logWarningsWhenNoCategoriesMatch="true">
<listeners>
<add source="Enterprise Library Logging" formatter="Text Formatter" log="Application" machineName="" listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.FormattedEventLogTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=2.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" traceOutputOptions="None" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.FormattedEventLogTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=2.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" name="Formatted EventLog TraceListener"/>
</listeners>
<formatters>
<add template="Timestamp: {timestamp}&#xA;Message: {message}&#xA;Category: {category}&#xA;Priority: {priority}&#xA;EventId: {eventid}&#xA;Severity: {severity}&#xA;Title:{title}&#xA;Machine: {machine}&#xA;Application Domain: {appDomain}&#xA;Process Id: {processId}&#xA;Process Name: {processName}&#xA;Win32 Thread Id: {win32ThreadId}&#xA;Thread Name: {threadName}&#xA;Extended Properties: {dictionary({key} - {value}&#xA;)}" type="Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.TextFormatter, Microsoft.Practices.EnterpriseLibrary.Logging, Version=2.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" name="Text Formatter"/>
</formatters>
<categorySources>
<add switchValue="All" name="General">
<listeners>
<add name="Formatted EventLog TraceListener"/>
</listeners>
</add>
</categorySources>
<specialSources>
<allEvents switchValue="All" name="All Events"/>
<notProcessed switchValue="All" name="Unprocessed Category"/>
<errors switchValue="All" name="Logging Errors &amp; Warnings">
<listeners>
<add name="Formatted EventLog TraceListener"/>
</listeners>
</errors>
</specialSources>
</loggingConfiguration>
<exceptionHandling>
<exceptionPolicies>
<add name="GlobalExceptionLogger">
<exceptionTypes>
<add type="System.Exception, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" postHandlingAction="None" name="Exception">
<exceptionHandlers>
<add logCategory="General" eventId="100" severity="Error" title="Enterprise Library Exception Handling" formatterType="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.TextExceptionFormatter, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=2.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" priority="0" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging.LoggingExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging, Version=2.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" name="Logging Handler"/>
</exceptionHandlers>
</add>
</exceptionTypes>
</add>
</exceptionPolicies>
</exceptionHandling>
...

Then in the <system.web> node, add the following, if they are not already there:

<assemblies>
<add assembly="System.Management, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
<add assembly="System.Configuration.Install, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
</assemblies>
</compilation>
<siteMap defaultProvider="DefaultSiteMapProvider" enabled="true">
<providers>
<add name="DefaultSiteMapProvider" type="Microsoft.Practices.CompositeWeb.Providers.ModuleSiteMapProvider, Microsoft.Practices.CompositeWeb" securityTrimmingEnabled="true"/>
</providers>
</siteMap>
<httpModules>
<add name="WebClientAuthorizationModule" type="Microsoft.Practices.CompositeWeb.Authorization.WebClientAuthorizationModule, Microsoft.Practices.CompositeWeb"/>
<add name="ExceptionLoggerHttpModule" type="Microsoft.Practices.CompositeWeb.EnterpriseLibrary.ExceptionLogger, Microsoft.Practices.CompositeWeb.EnterpriseLibrary" />
</httpModules>

That concludes the additions to the Web.Config file. This may require some merging of sections, so be careful.

Change your solution

Now we need to edit the Solution in Visual Studio.

Create a Solution Folder called "WebSites", and move your web site project into the new solution folder.

Next, we will add the Shell Module to the solution structure. First, add a Solution Folder to the root of the solution called Modules. In side this new solution folder, add an existing project, the shell module you copied earlier,

Next, we will add a few more references to your web siteproject. I strongly encourage you to use the Add Reference button on the web site's Property Pages.  If you just right click and use Add Reference from the context menu, you may experience some odd behaviors.  You will want references to all of the dlls in the Library folder we copied earlier. If you want to remove functionality later (like exception shielding) you can remove the references then. Also, add a reference to the Shell project we added.

Finally, we need to make some changes to the Global.asax file. If the project does not have one, copy the Global.asax from the sample Web Client Solution, and add the existing file to your site. If you have one, open it up.

There are a few changes to make:

Change the base class from System.Web.HttpApplication to Microsoft.Practices.CompositeWeb.WebClientApplication

If you have an Application_Start method and you do not need the sender and event arguments , rename it to

protected override void Start()

If you need the arguments, make sure the first line is

base.Application_Start(sender, e);

You may need to close and re-open the solution to enable the Add View (with Presenter) recipe. (I did, at least).

Next Steps

Now, you should be able to compile, use the recipes as is this solution was created with the WCSF, and run your web site. In the not too distant future, I will have other posts on what to do next.

Also, Mariano has a few tips for adding the WCSF to an existing site.