Configuring custom INavigationService in WCSF Contrib library (Updated)

I recently wrote about implementing a custom PageFlow navigation provider in the WCSF library.  Since then, the patterns team has broken out the PageFlow block into a separate package (formally the “Patterns & Practices: Web Client Software Factory Contrib“).

Since my existing PageFlow navigation provider-related modifications became obsolete with this change, I thought I’d spend a few minutes porting my changes to the new block.  I actually completed this task a couple of months ago, but have just now found the time to post it publicly.  Thanks to those who persevered in prodding me to put it online!

WCSF Changes

The web.config setting is identical to the original setting, marked up as:

<pageFlowProvider providerType="WCSFContrib.PageFlow.Xml.XmlPageFlowProvider, WCSFContrib.PageFlow.Xml" navigationType="[Fully Qualified Assembly Name]" />
This change is picked up through an addition to Configuration/PageFlowProviderSection.cs, as:
        /// <summary>
        /// Defines the concrete type of the INavigator.
        /// </summary>
        [ConfigurationProperty("navigationType", DefaultValue = "WCSFContrib.PageFlow.Services.NavigationService", IsRequired = false)]
        public string NavigationType
            {
            get { return (string)base["navigationType"]; }
            }

 

Next, the BuildProvider method of PageFlowDirectory (PageFlowDirectory.cs) must be augmented to pick up the specified provider and load it (changes in bold):

        private static IPageFlowProvider BuildProvider()
        {
            PageFlowProviderSection configSection =
                (PageFlowProviderSection) ConfigurationManager.GetSection("pageFlow/pageFlowProvider");
            PageFlowInstanceStoreProviderSection storeSection =
                (PageFlowInstanceStoreProviderSection) ConfigurationManager.GetSection("pageFlow/pageFlowInstanceStoreProvider");
            PageFlowInstanceCorrelationTokenProviderSection tokenProviderSection =
                (PageFlowInstanceCorrelationTokenProviderSection)ConfigurationManager.GetSection("pageFlow/pageFlowInstanceCorrelationTokenProvider");
            Type providerType = Type.GetType(configSection.ProviderType);
            Type navigationType = Type.GetType(configSection.NavigationType);

            if (navigationType == null)
                throw new InvalidOperationException("Invalid navigationType specified: " + configSection.NavigationType);

            Services.INavigationService navigationService = (Services.INavigationService)Activator.CreateInstance(navigationType);
            _provider = (IPageFlowProvider)Activator.CreateInstance(providerType,
                                                                    BindingFlags.CreateInstance,
                                                                    null,
                                                                    new object[] { navigationService, storeSection, tokenProviderSection },
                                                                    CultureInfo.CurrentCulture);
            return _provider;
        }

Finally, each concrete provider must be modified to accept the appropriate navigationService as a constructor parameter. In the case of the XmlPageFlowProvider, this would result in the following changes (highlighted in bold):

        public XmlPageFlowProvider(INavigationService navigationService, ...)
        {
            if (pageFlowInstanceStoreProviderSection == null)
                throw new ArgumentNullException("pageFlowInstanceStoreProviderSection");

            if (pageFlowCorrelationTokenProviderSection == null)
                throw new ArgumentNullException("pageFlowCorrelationTokenProviderSection");

            IDictionary<string, NavigationGraph> navigationGraphs = new WebConfigStore().GetXmlPageFlowNavigationGraphs();

            _pageFlowFactory = new XmlPageFlowFactory(navigationGraphs, navigationService);
            //_pageFlowFactory = new XmlPageFlowFactory(navigationGraphs, new NavigationService());

            Type tokenProviderType = Type.GetType(pageFlowCorrelationTokenProviderSection.ProviderType, true, true);
            IPageFlowCorrelationTokenProvider _tokenProvider =
                (IPageFlowCorrelationTokenProvider)Activator.CreateInstance(tokenProviderType);

            Type storeType = Type.GetType(pageFlowInstanceStoreProviderSection.ProviderType, true, true);
            _store =
                (IPageFlowInstanceStore)Activator.CreateInstance(storeType, BindingFlags.CreateInstance, null,
                                                                  new object[]
                                                                      {
                                                                          pageFlowInstanceStoreProviderSection.ConnectionStringName,
                                                                        _tokenProvider
                                                                      },
                                                                  CultureInfo.CurrentCulture);
            PopulateDirectory(navigationGraphs);
         }

As a footnote, I did consider modifying the NavigationService directly to instantiate and decorate an underlying custom NavigationProvider. While this might have resulted in one less code change, I do not feel that it is of optimal design. Concrete PageFlow providers should not be responsible for instantiation of their navigationProviders; these objects should be injected, for maximum flexibility. 

Downloads

For the complete (pre-modification) package, visit the project homepage at http://www.codeplex.com/wcsfcontrib.  For those keeping score, I’ve created a work item for this change here.

Coming Soon: Using the PageFlow Application Block within DotNetNuke

The WCSF PageFlow package continues to be an excellent application block.  It is a shame that it continues to be so ASPX-centric.  The modifications herein are a first step toward removing this limitation and allowing it to be used in more flexible circumstances, such as an ASCX-based state machine or within the DNN framework.

For anyone who is interested, I am now using this application block successfully (soon to be in production) within the DotNetNuke framework, using:

  • The block-level modifications outlined above
  • A custom navigation service (currently SelfRedirectingNavigationService, though, as I will blog about sometime soon, I have some enhancements in store in this area)
  • A web.config-defined XmlPageFlow provider state machine
  • A module shell with some custom wire-up code
  • One ASCX control for each state in the XmlPageFlow state machine

I will continue to blog about this topic, most likely addressing a concrete navigation service that may be used with DNN next.  Stay tuned!

B

Be Sociable, Share!

Comments are closed.

Log in