惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Recent Announcements
Recent Announcements
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
B
Blog
T
The Blog of Author Tim Ferriss
J
Java Code Geeks
腾讯CDC
D
Docker
G
Google Developers Blog
D
DataBreaches.Net
雷峰网
雷峰网
Blog — PlanetScale
Blog — PlanetScale
S
SegmentFault 最新的问题
The Cloudflare Blog
有赞技术团队
有赞技术团队
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Stack Overflow Blog
Stack Overflow Blog
大猫的无限游戏
大猫的无限游戏
量子位
美团技术团队
aimingoo的专栏
aimingoo的专栏
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Engineering at Meta
Engineering at Meta
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

博客园 - umlchina

.Net桌面程序自动更新NAppUpdate c# interview examination questions how to use wpf control in a winform application Localizing of WPF application Unity 中的拦截功能 Interception and Interceptors in C# (Aspect oriented programming) 转载 WPF Snippet Tutorial - Aligning ListView Items WPF Tutorial - How To Use A DataTemplateSelector Dependency Properties Introduction To Dependency Properties of WPF 朋友的公司招.net程序员2名及项目经理1名 Simple Object Access Protocol (SOAP) 1.1 how to create pdf by itextsharp convert RGB colorspace to Homochromy(Black/whit) - umlchina 图片处理转换 how to design a hardware service use .net remoting : 使用SAAJ发送和接收SOAP消息 consume an asp.net webservice(upload a file to server) from java via soap 紧急求助:我们想做一个service程序,这个service负责统一管理这台机器上装的一些设备(包括签字板,扫描仪等等),
prism框架里module太多启动速度过慢
umlchina · 2011-11-21 · via 博客园 - umlchina

一个基于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 生成模块配置文件。