Partilhar via


Dynamic Modularity QuickStarts

Retired Content

This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.

This guidance includes two QuickStarts that demonstrate how to build Windows Presentation Foundation applications composed of modules that are dynamically discovered and loaded at run time. Each QuickStart utilizes a different mechanism to discover modules. The QuickStarts are the following:

  • Dynamic Modularity (Configuration) QuickStart. This QuickStart demonstrates how to dynamically load modules using a configuration-based module enumerator. This module enumerator determines what modules should be loaded by reading a configuration section in the application's configuration file.
  • Dynamic Modularity (DirectoryLookup) QuickStart. This QuickStart demonstrates how to dynamically load modules using a directory lookup module enumerator. This module enumerator discovers modules by inspecting assemblies in a particular folder.

Scenario

Note

The scenario is the same for both Dynamic Modularity QuickStarts.

The QuickStarts are composed of four modules: ModuleA, ModuleB, ModuleC, and ModuleD. These modules are dynamically loaded; this means that the Shell does not contain a direct reference to the modules' assemblies; because of this, modules have to be discovered and loaded at run time.

Modules can have dependencies between them. In the QuickStarts, ModuleA depends on ModuleD, and ModuleD depends on ModuleB. Therefore, ModuleD needs to be loaded before ModuleA, and ModuleB needs to be loaded before ModuleD.

A module can also be loaded on demand. This means that the module does not need to be loaded when the application starts; instead, it can be loaded when a particular event during the application life occurs. In the Modularity QuickStarts, ModuleC is loaded on demand when the user clicks a button in a view exposed by ModuleB.

Figure 1 illustrates the main window for the QuickStarts (both QuickStarts share the same user interface).

Ff921068.42c10e05-a6a9-45bd-8c03-cb3f3462cf7f(en-us,PandP.10).png

Figure 1
Modularity QuickStarts’ user interface

Building and Running the QuickStarts

The QuickStarts ship as source code, which means you must compile them before running them. These QuickStarts do not have any prerequisites.

To build and run the Dynamic Modularity (Configuration) QuickStart

  1. Run the file Open QS-ConfigurationModularity Quickstart Solution.bat to open the solution in Visual Studio.
  2. Right-click the ConfigurationModularity project and then click Set as StartUp Project.
  3. On the Build menu, click Rebuild Solution.
  4. Press F5 to run the QuickStart.

To build and run the Dynamic Modularity (DirectoryLookup) QuickStart

  1. Run the file Open QS-DirectoryLookupModularity Quickstart Solution.bat to open the solution in Visual Studio.
  2. Right-click the DirectoryLookupModularity project and then click Set as StartUp Project.
  3. On the Build menu, click Rebuild Solution.
  4. Press F5 to run the QuickStart.

Walkthrough

Perform the following steps in any of the Dynamic Modularity QuickStarts to explore the scenario.

To explore the scenario

  1. Run one of the provided batch files:

    • Open QS-ConfigurationModularity Quickstart Solution.bat. This opens the solution file for the Dynamic Modularity (Configuration) QuickStart.
    • Open QS-DirectoryLookupModularity Quickstart Solution.bat. This opens the solution file for the Dynamic Modularity (DirectoryLookup) QuickStart.

    Note

    The walkthrough for both QuickStarts is equivalent; the QuickStarts take the same inputs and produce the same output. The only difference between both is the module enumeration mechanism utilized.

  2. On the Build menu, click Rebuild Solution.

  3. Press F5 to run the application. The main window shows a stack of views, each of which is loaded by a different module, as illustrated in Figure 2. Note that ModuleB's view includes a button to load the ModuleC.

    Ff921068.fe01762a-a2b5-4db1-80fb-8b62f617392c(en-us,PandP.10).png

    Figure 2
    QuickStart main window

    The order in which views appear in the window reflects the modules load order. The load order is determined by the dependencies between modules.

  4. Click the Load Module C button to load ModuleC. When ModuleC loads, it adds a view to the window, as shown in Figure 3.

    Ff921068.8ec8ba02-dd80-4466-90b5-6d20f4df3dbf(en-us,PandP.10).png

    Figure 3
    When the Load Module C button is clicked, ModuleC gets loaded

Implementation Details

The QuickStarts highlight the key components in dynamic modularity. The following sections describe the key artifacts of each QuickStart.

Dynamic Modularity (Configuration) QuickStart

The Dynamic Modularity (Configuration) QuickStart leverages the usage of the configuration module loading mechanism. This mechanism discovers modules by reading the application's configuration file.

Module Enumerator Setup

The Bootstrapper class inherits from the UnityBootstrapper abstract class. The UnityBootstrapper class has methods that create and configure the Unity container, configure the region adapters, create the shell and initialize the modules.

Note

Module retrieval and module initialization is automatically handled by the Bootstrapper’s InitializeModules base method. When customization is needed, the InitializeModules method can be overridden to adjust it to your needs.

The Bootstrapper class’ GetModuleEnumerator template method has to be overridden to specify which module enumerator will be used to load and initialize modules. The configuration module loading is specified by returning a new instance of the ConfigurationModuleEnumerator class in the GetModuleEnumerator method.

The ConfigurationModuleEnumerator constructor requires that an instance of the ConfigurationStore class is passed as a parameter to retrieve a valid instance of the modules configuration section from the configuration file.

The following code shows the overridden meth** GetModuleEnumerator where the ConfigurationModuleEnumerator** module enumerator is set up.

protected override IModuleEnumerator GetModuleEnumerator()
{
    ConfigurationStore store = new ConfigurationStore();
    return new ConfigurationModuleEnumerator(store);
}

Modules Configuration

When using the configuration module loading mechanism, modules are configured in the application's configuration file. The following code shows the application's configuration file (located at ConfigurationModularity\App.config).

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="modules" type="Microsoft.Practices.Composite.Modularity.ModulesConfigurationSection, Microsoft.Practices.Composite"/>
  </configSections>
  <modules>
    <module assemblyFile="Modules/ModuleD.dll" moduleType="ModuleD.ModuleD" moduleName="ModuleD">
      <dependencies>
        <dependency moduleName="ModuleB"/>
      </dependencies>
    </module>
    <module assemblyFile="Modules/ModuleB.dll" moduleType="ModuleB.ModuleB" moduleName="ModuleB"/>
    <module assemblyFile="Modules/ModuleA.dll" moduleType="ModuleA.ModuleA" moduleName="ModuleA">
      <dependencies>
        <dependency moduleName="ModuleD"/>
      </dependencies>
    </module>
    <module assemblyFile="Modules/ModuleC.dll" moduleType="ModuleC.ModuleC" moduleName="ModuleC" startupLoaded="false"/>
  </modules>
</configuration>

In the preceding code, four modules are configured within the modules section: ModuleA, ModuleB, ModuleC, and ModuleD. Note that modules ModuleD and ModuleA define dependencies on ModuleB and ModuleD, respectively, using the dependencies node.

This QuickStart has post-build events configured on each module project to automatically store the modules' assemblies in a folder after a successful build. A common folder for modules is recommended to simplify the application deployment. To see the post-build events configuration, right-click a module project, and then click Properties. In the Properties dialog box, click the Build Events tab. The following code shows the post-build event command in the Post-build event command line text box.

xcopy "$(TargetDir)*.*" "$(SolutionDir)ConfigurationModularity\bin\$(ConfigurationName)\Modules\" /Y

Loading Modules on Demand

This QuickStart demonstrates how to load a module on demand. A module is loaded on demand in reaction to an event during the application life cycle, instead of being loaded on application startup. In this QuickStart, the module ModuleC is configured to be loaded on demand.

The following code shows the module XML element for ModuleC in the application's configuration file (located at ConfigurationModularity\App.config). In this case, the **startupLoaded **attribute is set to false. This indicates the module should not be loaded on startup.

<module assemblyFile="Modules/ModuleC.dll" moduleType="ModuleC.ModuleC" moduleName="ModuleC" startupLoaded="false"/>

To load a module on demand, you first obtain the module's information by calling the GetModule method on the module enumerator, and then you initialize it by invoking the Initialize method on the module loader service.

The following code shows the DefaultViewB view implementation (located at ModuleB\DefaultViewB.xaml.cs), which exposes a button to load the ModuleC module on demand. The OnLoadModuleCClick method in the following code is the event handler for the click event of the button; it is in charge of loading the ModuleC module.

public partial class DefaultViewB : UserControl
{
    private readonly IModuleLoader moduleLoader;
    private readonly IModuleEnumerator moduleEnumerator;

    public DefaultViewB(IModuleLoader moduleLoader, IModuleEnumerator moduleEnumerator)
    {
        this.moduleLoader = moduleLoader;
        this.moduleEnumerator = moduleEnumerator;

        InitializeComponent();
    }

    private void OnLoadModuleCClick(object sender, RoutedEventArgs e)
    {
        moduleLoader.Initialize(moduleEnumerator.GetModule("ModuleC"));
    }
}

Note

Because the view contains a reference to the IModuleEnumerator interface and not to a concrete module enumerator implementation, the preceding code is the same for both Dynamic Modularity QuickStarts.

Dynamic Modularity (DirectoryLookup) QuickStart

The Dynamic Modularity (DirectoryLookup) QuickStart leverages the usage of the directory lookup module loading mechanism. This mechanism discovers modules by inspecting module assemblies in a folder.

Module Enumerator Setup

The Bootstrapper class inherits from the UnityBootstrapper abstract class. The UnityBootstrapper class has methods that create and configure the Unity container, configure the region adapters, create the shell and initialize the modules.

Note

Module retrieval and module initialization is automatically handled by the Bootstrapper’s InitializeModules base method. When customization is needed, the InitializeModules method can be overridden to adjust it to your needs.

The Bootstrapper class’ GetModuleEnumerator template method has to be overridden to specify which module enumerator will be used to load and initialize modules.

The DirectoryLookupModuleEnumerator class is selected as the module enumerator to be used, by returning a new instance of the DirectoryLookupModuleEnumerator class, supplying the folder path—where module assemblies are stored—as a constructor parameter. This class is responsible for discovering modules within a particular folder. By returning the instance, the base class will register it with the Unity container.

The following code shows the overridden method GetModuleEnumerator. This method is invoked when the application starts, and demonstrates how the DirectoryLookupModuleEnumerator module enumerator is set up.

protected override IModuleEnumerator GetModuleEnumerator()
{
    return new DirectoryLookupModuleEnumerator(@".\Modules");
}

Modules Configuration

Because this QuickStart uses the directory lookup mechanism, modules classes—classes that implement the IModule interface—have the Module attribute applied. The following code shows the class signature for ModuleA's module class.

[Module(ModuleName = "ModuleA")]
[ModuleDependency("ModuleD")]
public class ModuleA : IModule
{
    ...
}

As described in the section "Module Enumerator Setup" earlier in this topic, the directory lookup enumerator is configured to look for modules in the Modules folder. To automatically have the modules' assemblies in this folder after a successful build, module projects have a post-build event configured. To see the post-build events configuration, right-click a module project, and then click Properties. In the Properties dialog box, click the Build Events tab. The following code shows the post-build event command in the Post-build event command line text box.

xcopy "$(TargetDir)*.*" "$(SolutionDir)DirectoryLookupModularity\bin\$(ConfigurationName)\Modules\" /Y

Loading Modules on Demand

This QuickStart demonstrates how to load a module on demand. A module is loaded on demand in reaction to an event during the application life cycle, instead of being loaded on application startup. In this QuickStart, the module ModuleC is configured to be loaded on demand.

The following code shows the module class for ModuleC (located at ModuleC\ModuleC.cs). In this case, the Module attribute has the property StartupLoaded to false. This indicates the module should not be loaded on startup.

[Module(ModuleName = "ModuleC", StartupLoaded = false)]
public class ModuleC : IModule
{    ...
}

The following code shows the DefaultViewB view implementation (located at ModuleB\DefaultViewB.xaml.cs), which exposes a button to load the ModuleC module on demand. The OnLoadModuleCClick method in the following code is the event handler for the click event of the button; it is in charge of loading the ModuleC module.

public partial class DefaultViewB : UserControl
{
    private readonly IModuleLoader moduleLoader;
    private readonly IModuleEnumerator moduleEnumerator;

    public DefaultViewB(IModuleLoader moduleLoader, IModuleEnumerator moduleEnumerator)
    {
        this.moduleLoader = moduleLoader;
        this.moduleEnumerator = moduleEnumerator;

        InitializeComponent();
    }

    private void OnLoadModuleCClick(object sender, RoutedEventArgs e)
    {
        moduleLoader.Initialize(moduleEnumerator.GetModule("ModuleC"));
    }
}

Note

Note: Because the view contains a reference to the IModuleEnumerator interface and not to a concrete module enumerator implementation, the preceding code is the same for both Dynamic Modularity QuickStarts.

Acceptance Tests

The Dynamic Modularity QuickStarts include a separate solution that includes acceptance tests for both QuickStarts. Acceptance tests describe how an application should perform when you follow a series of steps; you can use the acceptance tests to explore the functional behavior of the applications in a variety of scenarios.

The acceptance tests were developed using the testing framework White. To run these tests, you need to have White installed. For more information about White, including download information, see White on Codeplex.

Note


The acceptance tests have been developed and verified with the White 0.1.5.0 release. Although other releases of White might work too, it is recommended to use the aforementioned release to avoid any issues when running the tests.

To run the UI Composition QuickStart acceptance tests

  1. Place the assemblies required by White in the folder Source\Lib\White. The files are the following:
    • Bricks.dll
    • Bricks.RuntimeFramework.dll
    • Castle.Core.dll
    • Castle.DynamicProxy2.dll
    • Core.dll
    • log4net.config
    • log4net.dll
    • nunit.framework.dll
    • White.NUnit.dll
    • Xstream.Core.dll
  2. In Visual Studio, open the solution file QuickStarts\Modularity\Modularity_AcceptanceTests.sln.
  3. Press F5.

Outcome

You should see the QuickStarts windows and the tests automatically interact with the user interface. At the end of the test pass, you should see that all tests have passed.

Retired Content

This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.