Module tag weirdness

Aug 14, 2008 at 8:29 PM
I'm playing around with using the ConfigurationModuleEnumerator.

For some reason, I can only seem to get the modules loaded properly if I give the assemblyFile tag the absolute path. If I give it a relative path (with ".." in it) it doesn't seem to be found. Also, the moduleType tag requires the complete namespace. Is this intended?

It seems odd that both moduleType and moduleName are both required. In the module, by default, moduleName is the same as moduleType, so you have to duplicate yourself in the configuration. If the ModuleName attribute is used, then I can see using moduleName but then not needing moduleType anymore. I can see an agrument for the requirement for having one of them, just not both. I can also see the argument for not needing either: just load all the classes that support the IModule interface. Any thoughts?

Thanks,
Harley
Aug 19, 2008 at 1:33 AM
Relative paths work - if you were to modify the QuickStarts / Modularity / ConfigurationModularity.sln and place the ModuleA, B, C and D DLL files into the bin folder (versus debug\modules) the application will work.

<?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="../ModuleD.dll" moduleType="ModuleD.ModuleD" moduleName="ModuleD">
      <dependencies>
        <dependency moduleName="ModuleB"/>
      </dependencies>
    </module>
    <module assemblyFile="../ModuleB.dll" moduleType="ModuleB.ModuleB" moduleName="ModuleB"/>
    <module assemblyFile="../ModuleA.dll" moduleType="ModuleA.ModuleA" moduleName="ModuleA">
      <dependencies>
        <dependency moduleName="ModuleD"/>
      </dependencies>
    </module>
    <module assemblyFile="../ModuleC.dll" moduleType="ModuleC.ModuleC" moduleName="ModuleC" startupLoaded="false"/>
  </modules>
</configuration>

As for the moduleType and moduleName it becomes apparent by the above the importance of the distinction, particularly since the type typically includes the namespace.

As for your argument for just loading all classes that support IModule, it is an argument won :) because it is incorporated and available - you can run the DirectoryLookupModularity solution (a peer to the ConfigurationModularity solution).   I also blog about it HERE



Aug 19, 2008 at 4:38 PM
Edited Aug 19, 2008 at 4:40 PM
Relative paths work - if you were to modify the QuickStarts / Modularity / ConfigurationModularity.sln and place the ModuleA, B, C and D DLL files into the bin folder (versus debug\modules) the application will work.

I must have had a typo, they're working for me now too.

As for the moduleType and moduleName it becomes apparent by the above the importance of the distinction, particularly since the type typically includes the namespace.

I must be dense as I don't see the apparent need for it above. Are you referring to it's use by the dependency tags? If so, I see two solutions: 1) either the dependency tag could optionally support moduleType 2) or the moduleName on the module tag could be inferred by the type (everything after the last period), just like the compiler does. (There's no need for the ModuleName attribute, it's inferred by the type.)

As for your argument for just loading all classes that support IModule, it is an argument won :) because it is incorporated and available - you can run the DirectoryLookupModularity solution (a peer to the ConfigurationModularity solution).

But I still want to control which files get loaded, I just don't want to have to specify each module in the file. So I still want to have a configuration file with module tags, just without moduleType and moduleName.

I also blog about it HERE.

I've been reading your blog before this; showed up in some Google searches. Interesting and good stuff, thanks for taking the time to write.

Aug 21, 2008 at 4:05 AM
Edited Aug 21, 2008 at 4:51 AM

[hpebley3] "I must be dense as I don't see the apparent need for it above."

No, not dense, outside of the context of a source code reference I guess it wouldn't make much sense.   To clarify I'll use the DirectoryLookupModuleEnumerator.cs file - specifically the GetModulesInfos() method which returns a type of ModuleInfo[]; ModuleInfo is the core structure used by the ModuleLoader and dependent processes.

-----[ excerpt from GetModuleInfos() ]------

Assembly[] alreadyLoadedAssemblies = AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies();
var modules = directory.GetFiles("*.dll")
    .Where(file => alreadyLoadedAssemblies
        .FirstOrDefault(assembly => String.Compare(Path.GetFileName(assembly.Location), file.Name, StringComparison.OrdinalIgnoreCase) == 0) == null)
    .SelectMany(file => Assembly.ReflectionOnlyLoadFrom(file.FullName)
                            .GetExportedTypes()
                            .Where(IModuleType.IsAssignableFrom)
                            .Where(t => t != IModuleType)
                            .Select(type => CreateModuleInfo(type)));

var array = modules.ToArray();
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= resolveEventHandler;
return array;

---------------------------------------

The CreateModuleInfo(type) method is responsible for processing each type sent to it by Assembly.ReflectionOnlyLoadFrom() - if you examine the code you'll see it parses the information and instantiates a ModuleInfo from it.  The end result is that "var array" will contain an array of ModuleInfo.

I processed a solution that has a single module (VisioToolModule) - a breakpoint after "var array = modules.ToArray()" results in the following:

- array                            {Microsoft.Practices.Composite.Modularity.ModuleInfo[1]}     Microsoft.Practices.Composite.Modularity.ModuleInfo[]
   -  [0]                           {Microsoft.Practices.Composite.Modularity.ModuleInfo}          Microsoft.Practices.Composite.Modularity.ModuleInfo
            AssemblyFile      "D:\\GWN\\MainShell\\bin\\Debug\\VisioToolModule.dll"           string
            DependsOn        Count =  0                                                                               {System.Collections.Generic.List<string>}
            ModuleName     "VisioToolModule"                                                                     string
            ModuleType      "ProcessSolutionModule.VisioToolModule"                                string
            StartupLoaded   true                                                                                            bool

If you'll review the ModuleLoader code you'll find that it uses each of the above ModuleInfo properties in different ways (beyond the scope of this message but somewhat apparent).  Bear in mind that your AssemblyFile (ddl name) may not match your ModuleName, i.e., I renamed my VisioToolModule to VisioToolModule1 the results follow:

- array                            {Microsoft.Practices.Composite.Modularity.ModuleInfo[1]}     Microsoft.Practices.Composite.Modularity.ModuleInfo[]
   -  [0]                           {Microsoft.Practices.Composite.Modularity.ModuleInfo}          Composite.Modularity.ModuleInfo
            AssemblyFile      "D:\\GWN\\MainShell\\bin\\Debug\\VisioToolModule.dll"           string
            DependsOn        Count = 0                                                                                 {System.Collections.Generic.List<string>}
            ModuleName     "VisioToolModule1"                                                                   string
            ModuleType      "ProcessSolutionModule.VisioToolModule1"                              string
            StartupLoaded   true                                                                                            bool

With the notion that the ModuleType could have been a very long namespace it is understandable that the ModuleName (which is used as a key throughout the ModuleLoader process) is parsed from the ModuleType (class name).

---

[hpebley3] "But I still want to control which files get loaded, I just don't want to have to specify each module in the file. So I still want to have a configuration file with module tags, just without moduleType and moduleName."

From my reflection days I know that you'll need the filename to load the assembly and to instantiate a type (dynamically) you'll need the complete namesspace (to include class name); this locks you into providing at least those two parameters; I don't think you can get away from loading a class dynamically without these... If you search the ModuleLoader.cs code for moduleName you'll find it uses it as a key for multiple processes/purposes.  I do see where moduleName could be dropped but in every instance where you see it being used (throughout the various classes) you would have to either use the ModuleType as a key or parse it for the key - which could amount to a lot of unnecessary processing.

In my humble opinion the P&P team did an efficient job in providing an extensible ModuleLoading process.