2010-08-23 50 views
30

我有一個引導程序,它通過ASP.NET MVC應用程序中的所有程序集查找實現IBootstrapperTask接口的類型,然後將它們註冊到國際奧委會承辦人。這個想法是,你可以文學上將你的IBootstrapperTasks放在任何地方,並組織你的項目。ASP.NET - AppDomain.CurrentDomain.GetAssemblies() - AppDomain重啓後丟失的程序集

規範引導程序:

public class Bootstrapper 
{ 
    static Bootstrapper() 
    { 
     Type bootStrapperType = typeof(IBootstrapperTask); 

     IList<Assembly> assemblies = AppDomain.CurrentDomain.GetAssemblies(); 

     List<Type> tasks = new List<Type>(); 

     foreach (Assembly assembly in assemblies) 
     { 
      var types = from t in assembly.GetTypes() 
         where bootStrapperType.IsAssignableFrom(t) 
          && !t.IsInterface && !t.IsAbstract 
         select t; 

      tasks.AddRange(types); 
     } 

     foreach (Type task in tasks) 
     { 
      if (!IocHelper.Container().Kernel.HasComponent(task.FullName)) 
      { 
       IocHelper.Container().AddComponentLifeStyle(
        task.FullName, task, LifestyleType.Transient); 
      } 
     } 
    } 

    public static void Run() 
    { 
     // Get all registered IBootstrapperTasks, call Execute() method 
    } 
} 

完全構建,AppDomain.CurrentDomain.GetAssemblies()返回所有組件在我的解決方案(包括所有的GAC一個人的,但這並不影響我)之後。

然而,如果在AppDomain重新啓動,或者我「反彈」 Web.Config文件(加空格和保存),靜態構造函數再次運行,但是當AppDomain.CurrentDomain.GetAssemblies()被調用時,大部分大會缺少包括一個包含我的IBootstrapperTask類型的文件。

我該如何解決這個問題?我想我可以在System.IO的/ bin目錄下手動加載所有的DLL,但是如果可能的話,寧願避免這種情況,或者這是唯一的方法嗎?我是否採取了正確的一般方法呢?

這是一個在.NET 4.0上運行的ASP.NET MVC 2.0應用程序,我在內置的Visual Studio 2010 Cassini Web服務器中遇到了這個問題,並且在Windows Server 2008上的集成管道模式下使用了IIS7.0。


編輯:我只是碰到這種來得如此張貼Difference between AppDomain.GetAssemblies and BuildManager.GetReferencedAssemblies它說在需要時自動在AppDomain只加載大會(例如,當從組裝方法/類是首先調用)。我想這可以解釋爲什麼大會在AppDomain.CurrentDomain.GetAssemblies()上失蹤,因爲Bootstrapper很早就運行了。

我發現,如果我有條件「東西」打來的電話丟失大會前的引導程序如:

public class MvcApplication : System.Web.HttpApplication 
{ 
    protected void Application_Start() 
    { 
     MyApp.MissingAssembly.SomeClass someClass = 
      new MyApp.MissingAssembly.SomeClass(); 

     Bootstrapper.Run(); 
    } 
} 

...這似乎解決了問題,但它是一個黑客攻擊的一位。

+0

尼斯問答!參觀了你的博客 - 你寫了一些好東西。 – 2012-10-15 19:39:46

回答

50

我瀏覽了ASP.NET MVC 2.0源代碼,並查找了如何實現AreaRegistration.RegisterAllAreas();。該行通常放入Global.asax Application_Start()方法中,並在內部針對實現AreaRegistration抽象類型的類型掃描所有程序集。這有點像我之後的行爲。

看來RegisterAllAreas()使得以BuildManager.GetReferencedAssemblies()呼叫,以及它是否夠MVC那麼它的不夠好,我:-)

我已經做了一些實驗和BuildManager.GetReferencedAssemblies(良好),甚至會挑即使沒有對Visual Studio解決方案中的任何項目的引用,也會將隨機DLL放入/ bin文件夾。所以它看起來比AppDomain.Current.GetAssemblies()更可靠。

我已經重寫我的大會定位器的代碼如下:

using System.Collections.Generic; 
using System.Collections.ObjectModel; 
using System.IO; 
using System.Linq; 
using System.Reflection; 
using System.Web; 
using System.Web.Compilation; 

public static class AssemblyLocator 
{ 
    private static readonly ReadOnlyCollection<Assembly> AllAssemblies; 
    private static readonly ReadOnlyCollection<Assembly> BinAssemblies; 

    static AssemblyLocator() 
    { 
     AllAssemblies = new ReadOnlyCollection<Assembly>(
      BuildManager.GetReferencedAssemblies().Cast<Assembly>().ToList()); 

     IList<Assembly> binAssemblies = new List<Assembly>(); 

     string binFolder = HttpRuntime.AppDomainAppPath + "bin\\"; 
     IList<string> dllFiles = Directory.GetFiles(binFolder, "*.dll", 
      SearchOption.TopDirectoryOnly).ToList(); 

     foreach (string dllFile in dllFiles) 
     { 
      AssemblyName assemblyName = AssemblyName.GetAssemblyName(dllFile); 

      Assembly locatedAssembly = AllAssemblies.FirstOrDefault(a => 
       AssemblyName.ReferenceMatchesDefinition(
        a.GetName(), assemblyName)); 

      if (locatedAssembly != null) 
      { 
       binAssemblies.Add(locatedAssembly); 
      } 
     } 

     BinAssemblies = new ReadOnlyCollection<Assembly>(binAssemblies); 
    } 

    public static ReadOnlyCollection<Assembly> GetAssemblies() 
    { 
     return AllAssemblies; 
    } 

    public static ReadOnlyCollection<Assembly> GetBinFolderAssemblies() 
    { 
     return BinAssemblies; 
    } 
} 
+0

我遇到了這個問題,並使用你的解決方案。投票!謝謝! – ajma 2011-01-11 09:09:31

+0

非常感謝!我一直被困在這個問題上。你節省了我的時間。 – Azat 2012-07-26 05:13:24

1

看起來像你解決了你自己的問題。

編輯:就個人而言,我實際上會枚舉程序集,逐個加載它們並查找接口。基於文件而不是AppDomain在做什麼。

+0

你的意思是最後一個代碼示例MyApp.MissingAssembly.SomeClass等?如果是這樣,這不是一個真正的解決方案,我不得不在Bootstrapper.Run()之前向不同的程序集添加虛擬調用。 – 2010-08-23 23:10:26

+0

我認爲他意味着你應該通過文件,明確加載它們。那麼你將不會依賴你是否碰巧在通話中加載了它。 – 2010-08-24 01:44:28