Archive for the 'Composite WPF' Category

How To: Load modules on demand that have dependencies using Composite WPF (Prism)

The Composite Application Guidance for WPF - June 2008 release brings out-of-the-box support to load modules on demand. The only thing that you need to do is set the StartupLoaded property of the module to false. This feature is provided by the following Module Enumerators classes:

  • The DirectoryLookupModuleEnumerator class that discovers modules in assemblies stored in a particular folder. This approach requires you to configure modules using attributes in code like the following:
    [Module(ModuleName = "ModuleB", StartupLoaded = false)]
    public class ModuleB : IModule
    {
        //…    
    }

    For more information see: Using the Directory Lookup Module Enumerator.

  • The ConfigurationModuleEnumerator class that discovers modules by reading the configuration file of the application. This approach requires a configuration file. The following code shows the content of a sample App.config file:
    <?xml version=1.0encoding=utf-8?>
    <configuration>
      <configSections>
        <section name=modulestype=Microsoft.Practices.Composite.Modularity.ModulesConfigurationSection, Microsoft.Practices.Composite/>
      </configSections>
      <modules>
        <module assemblyFile=Modules/ModuleA.dllmoduleType=ModuleA.ModuleAmoduleName=ModuleA/>
        <module assemblyFile=Modules/ModuleB.dllmoduleType=ModuleB.ModuleBmoduleName=ModuleBstartupLoaded=false />
      </modules>
    </configuration>

    For more information see: Using the Configuration Module Enumerator.

Specifying Modules Dependencies

The mentioned Module Enumerators allow you to specify dependencies on the modules. In this way the Module Loader initializes first all its dependent modules before loading it.

  • With the DirectoryLookupModuleEnumerator class the dependencies are specified with the ModuleDependecy attribute.
    [Module(ModuleName = "ModuleB", StartupLoaded = false)]
    [ModuleDependency("ModuleA")]
    public class ModuleB : IModule
    {
        //…    
    }
  • With the ConfigurationModuleEnumerator class the dependencies are specified with the depedencies node in the configuration file:
    <?xml version=1.0encoding=utf-8?>
    <configuration>
      <configSections>
        <section name=modulestype=Microsoft.Practices.Composite.Modularity.ModulesConfigurationSection, Microsoft.Practices.Composite/>
      </configSections>
      <modules>
        <module assemblyFile=Modules/ModuleA.dllmoduleType=ModuleA.ModuleAmoduleName=ModuleA/>
        <module assemblyFile=Modules/ModuleB.dllmoduleType=ModuleB.ModuleBmoduleName=ModuleBstartupLoaded=false” />
          <dependencies>
            <dependency moduleName=ModuleA/>
          </dependencies>
        </modules>
      </modules>
    </configuration>

The current release of the Composite Application Library do not bring support for loading modules on demand with dependencies. If you try to do this, you will get a ModuleLoadException exception with the message: A module declared a dependency on another module which is not declared to be loaded. Missing module(s).

Module Load Exception

Cause of the ModuleLoadException

To load a module the Composite Application Library provides the a Module Loader class. This class contains the Initialize method that receives the list of the modules that you want to load as a parameter.

public LoadModuleB(IModuleLoader moduleLoader, IModuleEnumerator moduleEnumerator)
{
    ModuleInfo[] modules = new ModuleInfo[] { moduleEnumerator.GetModule(“ModuleB”) };

    moduleLoader.Initialize(modules);
}

The Module Loader assumes that the list of modules passed to its Initialize method also includes the dependencies. That is why it throws the exception: A module declared a dependency on another module which is not declared to be loaded when you want to load a single module on demand with dependencies (the module’s dependencies are not present in the list).

Workaround

A workaround to solve this issue could be creating an extension method to the Module Enumerator class that returns the module and its dependencies. The following is a possible implementation:

using System.Linq;
using System.Collections.Generic;

// The namespace is important to make the extension method work.
namespace Microsoft.Practices.Composite.Modularity
{
    public static class ModuleEnumeratorExtensions
    {
        public static ModuleInfo[] GetModuleWithDependencies(this IModuleEnumerator moduleEnumerator, string moduleName)
        {
            List<ModuleInfo> moduleInfoList = new List<ModuleInfo>();
            ModuleInfo module = moduleEnumerator.GetModule(moduleName);
            moduleInfoList.Add(module);
            if (module.DependsOn != null)
            {
                foreach (string dependencyName in module.DependsOn)
                {
                    if (!moduleInfoList.Exists(existingModule => existingModule.ModuleName == dependencyName))
                    {
                        moduleInfoList.AddRange(GetModuleWithDependencies(moduleEnumerator, dependencyName));
                    }
                }
            }
            return moduleInfoList.ToArray();
        }
    }
}

Then, when you need to initialize a module on demand you can use the previous extension method in the following way:

public DefaultViewB(IModuleLoader moduleLoader, IModuleEnumerator moduleEnumerator)
{
    moduleLoader.Initialize( moduleEnumerator.GetModuleWithDependencies(“ModuleB”) );
}

 

Enjoy

WPF Quickstart (shipped with SCSF) regenerated using Composite WPF (Prism)

We published in the CompositeWPFContrib web site a new sample application. This application is the WPF Quickstart (shipped with the SCSF source code) regenerated using the Composite Application Guidance for WPF. We used this sample application while drafting the Composite Application Guidance for CAB Developers document.

You can find this sample in the last Change Set of the CompositeWPFContrib source code or download it from here.
DifferentComponents

This new sample also includes documentation about how the regeneration was performed. This document is available in the Documentation section of the CompositeWPFContrib web site:

Document

Related Resources

If you are interesting in migration scenarios you may find useful the following links:

 

How-to: Use the Disconnected Service Agent (DSA) with CompositeWPF (Prism)

A popular demand in Codeplex forums the past weeks has been related to working under temporarily connected scenarios. The Disconnected Service Agent Application Block that has been shipped with the SCSF since the May 2007 version was specially designed for this type of scenarios. Damian Schenkelman wrote a blog post overviewing the DSA to answer some of these questions.

Now that the Composite Application Guidance for WPF has been released, the issue became whether it was possible to use the DSA with Composite WPF. Therefore we decided to spend some time migrating the Disconnected Service Agent QuickStart from SCSF to its equal in Composite Application Library.

You can get the sample by downloading the latest change set of the CompositeWPF Contrib source control.

Using DSA in a Composite WPF application

All the Offline Blocks (Disconnected Service Agent, Endpoint Catalog and Connection Monitor) shipped with SCSF - May 2007 and higher versions do not have dependencies on CAB/SCSF.

To be able to use those blocks in a Composite WPF application you can do the following:

  1. Add the following configuration to the App.config file to configure the connection monitor and select the data storage that will be consumed by the DSA:
    <configuration>
      <configSections>
        <section name="ConnectionMonitor"
                 type="Microsoft.Practices.SmartClient.ConnectionMonitor.Configuration.ConnectionSettingsSection, Microsoft.Practices.SmartClient.ConnectionMonitor" />
        <section name="dataConfiguration"
                 type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data" />
      </configSections>
      <connectionStrings>
        <add name="QueueDatabase" connectionString="YourConnectionString" providerName="YourProvider" />
      </connectionStrings>
      <ConnectionMonitor>
        <Networks>
          <!– To check for internet connectivity–>
          <add Name="Internet" Address="http://www.google.com" />
        </Networks>
      </ConnectionMonitor>
      <dataConfiguration defaultDatabase="QueueDatabase">
        <providerMappings>
          <add databaseType="Microsoft.Practices.SmartClient.EnterpriseLibrary.SmartClientDatabase, Microsoft.Practices.SmartClient.EnterpriseLibrary"
               name="System.Data.SqlServerCe" />
        </providerMappings>
      </dataConfiguration>
    </configuration>

  2. Set up the Request Manager by overriding the ConfigureContainer method of your application’s Bootstrapper class.
    protected override void ConfigureContainer()
    {
        // Set up request manager.
        RequestManager requestManager = DatabaseRequestManagerIntializer.Initialize();
        requestManager.StartAutomaticDispatch();
    
        // Add the request queue to the Container. This queue will be used by service agents to enqueue requests.
        Container.RegisterInstance<IRequestQueue>(requestManager.RequestQueue, new ContainerControlledLifetimeManager());
    
        // Add the connection monitor to the Container. It will be used to determine connectivity status and provide    // feedback to the user accordingly.
        Container.RegisterInstance<IConnectionMonitor>(requestManager.ConnectionMonitor, new ContainerControlledLifetimeManager());
    
        base.ConfigureContainer();
    }

  3. Create an Agent service class that uses the IRequestQueue to define the offline behavior of your web service (in SCSF this is performed by the Create a Disconnected Service Agent recipe).
  4. Register an instance of your Agent class into your container. You can do this in your Module class.
    public void Initialize()
    {
        RegisterTypesAndServices();
    
        // Add your module views…
    }
    
    private void RegisterTypesAndServices()
    {
        Container.RegisterType<Agent>(new ContainerControlledLifetimeManager());
    }

Now you are able to inject your service agent in your classes.

Enjoy.