prism框架里module太多启动速度过慢

一个基于prism框架desktop项目,总共有100多个module。程序启动的时候有几秒钟的delay。

用的DirectoryModuleCatalog类从本地目录中装载module的程序集。

附:DirectoryModuleCatalog装载部分源代码

View Code
//===================================================================================
// Microsoft patterns & practices
// Composite Application Guidance for Windows Presentation Foundation and Silverlight
//===================================================================================
// Copyright (c) Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===================================================================================
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious.  No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
//===================================================================================
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Policy;
using Microsoft.Practices.Prism.Properties;

namespace Microsoft.Practices.Prism.Modularity
{
    /// <summary>
    
/// Represets a catalog created from a directory on disk.
    
/// </summary>
    
/// <remarks>
    
/// The directory catalog will scan the contents of a directory, locating classes that implement
    
/// <see cref="IModule"/> and add them to the catalog based on contents in their associated <see cref="ModuleAttribute"/>.
    
/// Assemblies are loaded into a new application domain with ReflectionOnlyLoad.  The application domain is destroyed
    
/// once the assemblies have been discovered.
    
/// 
    
/// The diretory catalog does not continue to monitor the directory after it has created the initialze catalog.
    
/// </remarks>
    public class DirectoryModuleCatalog : ModuleCatalog
    {
        /// <summary>
        
/// Directory containing modules to search for.
        
/// </summary>
        public string ModulePath { getset; }

        /// <summary>
        
/// Drives the main logic of building the child domain and searching for the assemblies.
        
/// </summary>
        protected override void InnerLoad()
        {
            if (string.IsNullOrEmpty(this.ModulePath))
                throw new InvalidOperationException(Resources.ModulePathCannotBeNullOrEmpty);

            if (!Directory.Exists(this.ModulePath))
                throw new InvalidOperationException(
                    string.Format(CultureInfo.CurrentCulture, Resources.DirectoryNotFound, this.ModulePath));

            AppDomain childDomain = this.BuildChildDomain(AppDomain.CurrentDomain);

            try
            {
                List<string> loadedAssemblies = new List<string>();

                var assemblies = (
                                     from Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()
                                     where !(assembly is System.Reflection.Emit.AssemblyBuilder)
                                        && assembly.GetType().FullName != "System.Reflection.Emit.InternalAssemblyBuilder"
                                        && !String.IsNullOrEmpty(assembly.Location)
                                     select assembly.Location
                                 );

                loadedAssemblies.AddRange(assemblies);

                Type loaderType = typeof(InnerModuleInfoLoader);

                if (loaderType.Assembly != null)
                {
                    var loader =
                        (InnerModuleInfoLoader)
                        childDomain.CreateInstanceFrom(loaderType.Assembly.Location, loaderType.FullName).Unwrap();
                    loader.LoadAssemblies(loadedAssemblies);
                    this.Items.AddRange(loader.GetModuleInfos(this.ModulePath));
                }
            }
            finally
            {
                AppDomain.Unload(childDomain);
            }
        }


        /// <summary>
        
/// Creates a new child domain and copies the evidence from a parent domain.
        
/// </summary>
        
/// <param name="parentDomain">The parent domain.</param>
        
/// <returns>The new child domain.</returns>
        
/// <remarks>
        
/// Grabs the <paramref name="parentDomain"/> evidence and uses it to construct the new
        
/// <see cref="AppDomain"/> because in a ClickOnce execution environment, creating an
        
/// <see cref="AppDomain"/> will by default pick up the partial trust environment of 
        
/// the AppLaunch.exe, which was the root executable. The AppLaunch.exe does a 
        
/// create domain and applies the evidence from the ClickOnce manifests to 
        
/// create the domain that the application is actually executing in. This will 
        
/// need to be Full Trust for Composite Application Library applications.
        
/// </remarks>
        
/// <exception cref="ArgumentNullException">An <see cref="ArgumentNullException"/> is thrown if <paramref name="parentDomain"/> is null.</exception>
        protected virtual AppDomain BuildChildDomain(AppDomain parentDomain)
        {
            if (parentDomain == nullthrow new System.ArgumentNullException("parentDomain");

            Evidence evidence = new Evidence(parentDomain.Evidence);
            AppDomainSetup setup = parentDomain.SetupInformation;
            return AppDomain.CreateDomain("DiscoveryRegion", evidence, setup);
        }

        private class InnerModuleInfoLoader : MarshalByRefObject
        {
            [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance""CA1822:MarkMembersAsStatic")]
            internal ModuleInfo[] GetModuleInfos(string path)
            {
                DirectoryInfo directory = new DirectoryInfo(path);

                ResolveEventHandler resolveEventHandler =
                    delegate(object sender, ResolveEventArgs args) { return OnReflectionOnlyResolve(args, directory); };

                AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += resolveEventHandler;

                Assembly moduleReflectionOnlyAssembly =
                    AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies().First(
                        asm => asm.FullName == typeof(IModule).Assembly.FullName);
                Type IModuleType = moduleReflectionOnlyAssembly.GetType(typeof(IModule).FullName);

                IEnumerable<ModuleInfo> modules = GetNotAllreadyLoadedModuleInfos(directory, IModuleType);

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

            private static IEnumerable<ModuleInfo> GetNotAllreadyLoadedModuleInfos(DirectoryInfo directory, Type IModuleType)
            {
                List<FileInfo> validAssemblies = new List<FileInfo>();
                Assembly[] alreadyLoadedAssemblies = AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies();

                var fileInfos = directory.GetFiles("*.dll")
                    .Where(file => alreadyLoadedAssemblies
                                       .FirstOrDefault(
                                       assembly =>
                                       String.Compare(Path.GetFileName(assembly.Location), file.Name,
                                                      StringComparison.OrdinalIgnoreCase) == 0) == null);
                
                foreach (FileInfo fileInfo in fileInfos)
                {
                    Assembly assembly = null;
                    try
                    {
                        assembly = Assembly.ReflectionOnlyLoadFrom(fileInfo.FullName);
                        validAssemblies.Add(fileInfo);
                    }
                    catch (BadImageFormatException)
                    {
                        // skip non-.NET Dlls
                    }
                }

                return validAssemblies.SelectMany(file => Assembly.ReflectionOnlyLoadFrom(file.FullName)
                                            .GetExportedTypes()
                                            .Where(IModuleType.IsAssignableFrom)
                                            .Where(t => t != IModuleType)
                                            .Where(t => !t.IsAbstract)
                                            .Select(type => CreateModuleInfo(type)));
            }

            private static Assembly OnReflectionOnlyResolve(ResolveEventArgs args, DirectoryInfo directory)
            {
                Assembly loadedAssembly = AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies().FirstOrDefault(
                    asm => string.Equals(asm.FullName, args.Name, StringComparison.OrdinalIgnoreCase));
                if (loadedAssembly != null)
                {
                    return loadedAssembly;
                }
                AssemblyName assemblyName = new AssemblyName(args.Name);
                string dependentAssemblyFilename = Path.Combine(directory.FullName, assemblyName.Name + ".dll");
                if (File.Exists(dependentAssemblyFilename))
                {
                    return Assembly.ReflectionOnlyLoadFrom(dependentAssemblyFilename);
                }
                return Assembly.ReflectionOnlyLoad(args.Name);
            }

            [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance""CA1822:MarkMembersAsStatic")]
            internal void LoadAssemblies(IEnumerable<string> assemblies)
            {
                foreach (string assemblyPath in assemblies)
                {
                    try
                    {
                        Assembly.ReflectionOnlyLoadFrom(assemblyPath);
                    }
                    catch (FileNotFoundException)
                    {
                        // Continue loading assemblies even if an assembly can not be loaded in the new AppDomain
                    }
                }
            }

            private static ModuleInfo CreateModuleInfo(Type type)
            {
                string moduleName = type.Name;
                List<string> dependsOn = new List<string>();
                bool onDemand = false;
                var moduleAttribute =
                    CustomAttributeData.GetCustomAttributes(type).FirstOrDefault(
                        cad => cad.Constructor.DeclaringType.FullName == typeof(ModuleAttribute).FullName);

                if (moduleAttribute != null)
                {
                    foreach (CustomAttributeNamedArgument argument in moduleAttribute.NamedArguments)
                    {
                        string argumentName = argument.MemberInfo.Name;
                        switch (argumentName)
                        {
                            case "ModuleName":
                                moduleName = (string) argument.TypedValue.Value;
                                break;

                            case "OnDemand":
                                onDemand = (bool) argument.TypedValue.Value;
                                break;

                            case "StartupLoaded":
                                onDemand = !((bool) argument.TypedValue.Value);
                                break;
                        }                           
                    }
                }

                var moduleDependencyAttributes =
                    CustomAttributeData.GetCustomAttributes(type).Where(
                        cad => cad.Constructor.DeclaringType.FullName == typeof(ModuleDependencyAttribute).FullName);

                foreach (CustomAttributeData cad in moduleDependencyAttributes)
                {
                    dependsOn.Add((string) cad.ConstructorArguments[0].Value);
                }

                ModuleInfo moduleInfo = new ModuleInfo(moduleName, type.AssemblyQualifiedName)
                                            {
                                                InitializationMode =
                                                    onDemand
                                                        ? InitializationMode.OnDemand
                                                        : InitializationMode.WhenAvailable,
                                                Ref = type.Assembly.CodeBase,
                                            };
                moduleInfo.DependsOn.AddRange(dependsOn);
                return moduleInfo;
            }
        }
    }
}

 

初步怀疑是在这里花费了太多的时间。

解决方式:

准备使用xaml文件来存储modules的配置信息。第一次启动程序当模块装载成功以后,创建一个xaml配置文件。

以后启动程序不再遍历目录,之间从xaml文件读取配置信息。

 

代码如下:

 

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.Prism.Modularity;
using System.IO;

namespace ModularityWithUnity.Desktop
{
    public class ModuleCatalogConfigXAML
    {
        private const string SPACE_STRING_4 = "    ";
        private const string SPACE_STRING_8 = "        ";
        private ModuleCatalog moduleCatalog;
        private StringBuilder sb;

        public ModuleCatalogConfigXAML(ModuleCatalog moduleCatalog)
        {
            sb = new StringBuilder();
            this.moduleCatalog = moduleCatalog;
        }

        public void GenerateXAMLConfigFile(string fileFullPath)
        {
            BuildHeader();
            BuildGroups();
            BuildByGroupless();
            BuildFooter();
            SaveAsFile(fileFullPath, sb.ToString());
        }

        private static void SaveAsFile(string savedPath, string content)
        {
            
            using (StreamWriter sw = File.CreateText(savedPath))
            {
                sw.Write(content);
            }
        }

        private void BuildHeader()
        {
            sb.AppendLine("<Modularity:ModuleCatalog xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"");
            sb.AppendLine(SPACE_STRING_8 +"xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"");
            sb.AppendLine(SPACE_STRING_8 + "xmlns:sys=\"clr-namespace:System;assembly=mscorlib\"");
            sb.AppendLine(SPACE_STRING_8 + "xmlns:Modularity=\"clr-namespace:Microsoft.Practices.Prism.Modularity;assembly=Microsoft.Practices.Prism\">");

        }

        private void BuildFooter()
        {
            sb.AppendLine("</Modularity:ModuleCatalog>");
        }

        private void BuildGroups()
        {
            foreach (var group in moduleCatalog.Groups)
            {
                BuildByGroup(group);
            }
        }

        private void BuildByGroup(ModuleInfoGroup group)
        {
            //IEnumerable<ModuleInfoGroup> groups = this.moduleCatalog.Groups;
            sb.AppendLine(SPACE_STRING_4 + "<Modularity:ModuleInfoGroup ");
            if (!string.IsNullOrEmpty(group.Ref))
            {
                sb.Append(" Ref=\"" + group.Ref + "\"");
            }

            sb.Append(" InitializationMode=\"" + group.InitializationMode.ToString() +"\">");
            IEnumerator<ModuleInfo> moduleInfos = group.GetEnumerator();
            
            while(moduleInfos.MoveNext())
            {
                BuildModule(moduleInfos.Current,SPACE_STRING_8);
            }
        }

        private void BuildByGroupless()
        {
            IEnumerable<ModuleInfo> groupLessModules = this.moduleCatalog.Items.OfType<ModuleInfo>();
            foreach (var moduleInfo in groupLessModules)
            {
                BuildModule( moduleInfo,SPACE_STRING_4);
            }
        }

        private void BuildModule(ModuleInfo moduleInfo,string space)
        {
            sb.AppendLine(space + "<!-- Module info without a group -->");

            string content = space + "<Modularity:ModuleInfo ";

            
            if (!string.IsNullOrEmpty(moduleInfo.Ref))
            {
                content += " Ref=\"" + moduleInfo.Ref + "\"";
            }

            if (!string.IsNullOrEmpty(moduleInfo.ModuleName))
            {
                content += " ModuleName=\"" + moduleInfo.ModuleName + "\"";
            }

            content += " ModuleType=\"" + moduleInfo.ModuleType + "\" >";

            sb.AppendLine(content);

            if (moduleInfo.DependsOn != null && moduleInfo.DependsOn.Count>0)
            {
                sb.AppendLine(space + SPACE_STRING_4 + "<Modularity:ModuleInfo.DependsOn>");
                foreach (var item in moduleInfo.DependsOn)
                {
                    sb.AppendLine(space + SPACE_STRING_4 + SPACE_STRING_4 + "<sys:String>" + item + "</sys:String>");
                }

                sb.AppendLine(space + SPACE_STRING_4 + "</Modularity:ModuleInfo.DependsOn>");
                sb.AppendLine(space + "</Modularity:ModuleInfo> ");
            }
           
            sb.AppendLine(space + "</Modularity:ModuleInfo> ");
                //sb.Append(" />");
            
        }
    }
}

 

使用的时候:

UnityBootstrapper

 

 1.虚方法CreateModuleCatalog 用来返回ModuleCatalog类。
  在此方法中添加逻辑如下:
如果modules的配置文件已经存在,则通过配置文件来生成ModuleCatalog.
2.虚方法InitializeModules装载模块信息,在此方法后调用GenerateXAMLConfigFile 生成模块配置文件。
 

 

posted @ 2011-11-21 14:55  umlchina  阅读(1020)  评论(2编辑  收藏  举报