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 application projects, and then how to convert the site from the current architecture to the WCSF architecture. To do this, we need to do a few things:
Today, I'm going to tackle the first two steps on a really simple web application project. There will be follow-ups to this post explaining how to do the same to handle web site projects. 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). If nothing else, this article wil show you why we automate creating a new solution the way we do, as there are a quite a few things to worry about.
I am starting with a basic web application project, with one web page. You could start with a copy of your existing web site, which you should back it up, just in case something here does not work. Either way, the instructions will be the same.
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 = WebClientApplication1 IsWCSFSolutionWAP = True IsWCSFSolution = TrueEndGlobalSection
I placed this right above the line
EndGlobal
Save and close the SLN file.
Then, edit the .csproj file for your web application project and add the following bolded line to the node
<ProjectExtensions> <VisualStudio> <FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}"> <!-- SNIP there is a lot of info here removed for space considerations--> </FlavorProperties> <UserProperties IsWebProject="true" /> </VisualStudio> </ProjectExtensions>
Now, you can open your solution in Visual Studio. Go to Tools-> Guidance Package Manager, and enable the "Web Client Development (WAP)" Guidance Package. (If you don't have that, you will need to download, compile, and register it from the WCSF CodePlex Community site.) Once the guidance package is enabled, you should be able to right-click on the web application project and see the "Web Client Factory" menu with "Add View (with presenter)"
Now, we can enable CWAB on your site, so dependency injection will work, and the new modules and pages will work without throwing exceptions.
This is simplest if you use the GP and create a new "Web Client Solution (Web Application Project)" 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.
Your solution folder on the file system should look something like this:
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}
Message: {message}
Category: {category}
Priority: {priority}
EventId: {eventid}
Severity: {severity}
Title:{title}
Machine: {machine}
Application Domain: {appDomain}
Process Id: {processId}
Process Name: {processName}
Win32 Thread Id: {win32ThreadId}
Thread Name: {threadName}
Extended Properties: {dictionary({key} - {value}
)}" 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 & 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> …..
<configuration>
<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}
Message: {message}
Category: {category}
Priority: {priority}
EventId: {eventid}
Severity: {severity}
Title:{title}
Machine: {machine}
Application Domain: {appDomain}
Process Id: {processId}
Process Name: {processName}
Win32 Thread Id: {win32ThreadId}
Thread Name: {threadName}
Extended Properties: {dictionary({key} - {value}
)}" 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 & 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:
<system.web>
<compilation debug="true"> <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.
Next, we will add a few more references to your web application project. 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, add a "Global Application File". If you have one, open it up.There are a few changes to make:
System.Web.HttpApplication
Microsoft.Practices.CompositeWeb.WebClientApplication
protected override void Start()
base.Application_Start(sender, e);