C#动态加载程序集非常慢

时间:2014-11-12 11:36:47

标签: c# .net windows-services dynamic-loading

我有一个用C#(.NET 3.5)编写的Windows服务,这个服务的作用是接收带有特定接口的DLL并执行它们的方法。

有些客户抱怨有时服务无法启动,在我查看我们的日志后,我注意到在尝试动态加载程序集时的部分需要花费大量时间(约〜 30-40秒),因此服务在60秒后尝试启动时会超时。

这里的主要问题是这个问题并不总是发生。我们很少能在QA环境中重现这种缓慢,所以当我试图重构动态加载的代码时,我真的不知道我是否正在解决这个问题。

目前代码正在加载2个DLL,并且对于每个程序集,我们获得所有引用的程序集,以便以递归方式动态加载程序集树。

我还可以看到,当我尝试加载.NET System.DataSystem.Core之类的.NET程序集时,我会在递归时加载,我忘记了因为它们不是在我的目录中。这似乎是一个主要的耗时。还有更好的建议吗?

/// <summary>
/// Loads all the assemblies and classes needed to run methods dinamically.
/// </summary>
public class BLService
{
  public static Dictionary<string, Assembly>    AssemblyHistory { get; private set; }

  static BLService()
  {
      // assembly
      Assembly assemblyToLoad = LoadAssembly(System.Configuration.ConfigurationManager.AppSettings[settingKey]);

      // load assembly dependencies
      foreach (var dependencyAssemblyName in assemblyToLoad.GetReferencedAssemblies())
          DynamicallyLoadAssembly(dependencyAssemblyName);
  }

  private static Assembly DynamicallyLoadAssembly(AssemblyName dependencyAssemblyName)
  {
      LCTracer.Instance.WriteToLog(string.Format("Loading assembly | Trying to dynamically load assembly {0}", dependencyAssemblyName.Name), LCTracer.ProfileAction.Start);
     Assembly currentAssembly = LoadAssembly(dependencyAssemblyName.Name);
     if (currentAssembly != null)
     {
         LCTracer.Instance.WriteToLog(string.Format("Loading assembly | Iterating on Assembly {0} references", dependencyAssemblyName.Name));
        foreach (var assembly in currentAssembly.GetReferencedAssemblies())
        {
           DynamicallyLoadAssembly(assembly);
        }
        LCTracer.Instance.WriteToLog(string.Format("Loading assembly | Finished iterating on Assembly {0} references", dependencyAssemblyName.Name));
      }
      LCTracer.Instance.WriteToLog(string.Format("Loading assembly"), LCTracer.ProfileAction.End);
     return currentAssembly;
  }

  /// <summary>
  /// Loads an assembly
  /// </summary>
  /// <param name="assemblyName"></param>
  /// <returns></returns>
  private static Assembly LoadAssembly(string assemblyName)
  {
     string assembliesDir = System.Configuration.ConfigurationManager.AppSettings["AssemblyPath"];
     string assemblyPath = Path.Combine(assembliesDir, assemblyName + ".dll");

     // We only load files from inside the designated directory.
     if (!File.Exists(assemblyPath))
     {
         LCTracer.Instance.WriteToLog("Loading assembly | " + assemblyName + " does not exist in path");
        return null;
     }

     byte[] assemblyByteArray = File.ReadAllBytes(assemblyPath);
     Assembly asm = null;

     try
     {
        asm = Assembly.Load(assemblyByteArray);

        // Load only if already loaded.
        if (!AssemblyHistory.ContainsKey(asm.GetName().Name))
        {
           AssemblyHistory.Add(asm.GetName().Name, asm);
           LCTracer.Instance.WriteToLog("Loading assembly | Success: Adding Assembly " + assemblyName + " to history.");
        }
     }
     catch (Exception ex)
     {
         LCTracer.Instance.WriteToLog("Loading assembly | Error: Adding Assembly " + assemblyName + " failed: message - " + ex.Message + " ,stack trace -  " + ex.StackTrace + " ,inner exception - " + ex.InnerException, null, LCTracer.LogDebugLevel.Low);

     }
     return (asm != null) ? AssemblyHistory[asm.GetName().Name] : AssemblyHistory[assemblyName];
  }
}

编辑:为上下文添加了一些代码。您可以将此服务视为插件运行器,它获取插件并执行它。 任何帮助将不胜感激。

2 个答案:

答案 0 :(得分:0)

尝试在抱怨客户端的服务器上安装Fusion日志查看器(随Windows SDK一起提供),并将其设置为记录所有加载尝试(也是成功的)。 然后你可以看到它在哪里寻找组件 - 你可能会发现那里有一些缓慢的网络路径或其他问题。

MSDN on Fusion Log Viewer

usefull blog post on using Fusion Log Viewer

答案 1 :(得分:0)

我找到了解决方案!

我尝试加载的程序集之一是NHibernate.XmlSerializers.dll,它是在运行时创建的,如果没有找到,在慢速机器上这个创建需要花费很多时间。

Here是一篇关于如何预生成此dll以提高性能的文章。

主要命令是: sgen.exe NHibernate.dll /type:NHibernate.Cfg.MappingSchema.HbmMapping /compiler:/keyfile:NHibernate.snk