2013-05-01 100 views
5

我在C#.NET 4.0 Windows Service應用程序中使用ILMerge和Quartz.NET。該應用運行良好,但沒有使用ILMerge,但現在我們已經接近發貨版本,我想將所有DLL合併成一個可執行文件。可執行文件失敗,出現奇怪異常

問題是,ILMerge似乎工作正常,但是當我運行的綜合可執行文件,它拋出此異常:

未處理的異常:Quartz.SchedulerException:線程池型「Quartz.Simpl.SimpleThreadPool」可能不被實例化。 ---> System.InvalidCastException:無法將類型爲「Quartz.Simpl.SimpleThreadPool」的對象轉換爲鍵入「Quartz.Spi.IThreadPool」。
在Quartz.Util.ObjectUtils.InstantiateType [T](類型型):在Quartz.Impl.StdSchedulerFactory.Instantiate(行0
):0行
---內部異常堆棧跟蹤結束 - -
在Quartz.Impl.StdSchedulerFactory.Instantiate()中:在0線
在Quartz.Impl.StdSchedulerFactory.GetScheduler():0行

有沒有人有任何想法,這是爲什麼?我已經浪費了4個小時了,我無法弄清楚。如果我沒有與ILMerge結合,那麼一切運行良好(與Quartz.dll和Common.Logging.dll在同一目錄中)。

我敢肯定有人必須嘗試包裝Quartz.net之前,像這樣的任何想法?

+0

這是您第一次嘗試將它與ILMerge結合嗎?還是在最近的變化之前起作用? – 2013-05-01 18:13:36

+1

我第一次嘗試使用ILMerge,運行它,不再工作。想象它一定是ILMerge,嘗試了內部標誌,沒有改變任何東西。刪除ILMerge,編譯通常(像我以前試過這樣),所有的作品(如果DLL在同一個目錄中)。 – 2013-05-01 18:23:33

+0

ILMerge無法處理的其中一件事是從外部程序集中進行類型加載(這可能是基於對堆棧跟蹤進行掃描的情況)。也許看看也可以找到其中一個替代方法[這裏](http://chrisghardwick.blogspot.nl/2012/01/ilmerge-getting-started-merging-and.html) – rene 2013-05-01 18:24:18

回答

1

免責聲明:我根本不知道Quartz.NET,儘管我花了一些時間在ILMerge上掙扎。當我終於明白其侷限性時,我停止使用它。

ILMerge'd應用程序往往會遇到包含「反射」一詞的所有問題。 我可以猜測(我從來沒有使用過Quartz.NET)一些類是使用反射解析並由配置文件驅動的。

類不僅通過它的名稱(帶有名稱空間),而且通過彙編它來自(不幸的是,它不會顯示在異常消息中)。因此,假設您已經(在ILMerging之前)有兩個程序集A(對於您的應用程序)和Q(對於Quartz.NET)。 組件'A'引用程序集'Q'並且正在使用實施'Q:QIntf'的'Q:QClass'類。 合併後,這些類變成'A:QClass'和'A:QIntf'(它們從程序集Q移到A),代碼中的所有引用都被替換爲使用那些(完全)新的類/接口,所以「答:QClass「現在正在實施」A:QIntf「。 但是,它沒有改變任何可能仍然引用「Q:QClass」的配置文件/嵌入字符串。

因此,當應用程序正在讀取那些未更新的配置文件時,它仍會加載「Q:QClass」(爲什麼它會發現它是一個不同的問題,也許你在當前文件夾中保留了「Q」組件或者它可能位於GAC - 見1)。 無論如何,「Q:QClass」並不實現「A:QIntf」,即使它們是二進制相同的,它仍然實現「Q:QIntf」 - 所以你不能將Q:QClass轉換爲A:QIntf。

不理想但工作的解決方案是「嵌入」程序集,而不是「合併」它們。我寫了一個開源工具(嵌入而不是合併),但它與這個問題沒有關係。所以如果你決定嵌入只是問我。

  1. 您可以通過在您的PC上刪除(隱藏,無論適用於您)Q.dll的每個實例來測試它。如果我是對的,現在例外應該說'FileNotFound'。
+0

我最終沒有使用ILMERGE,它只是不起作用,似乎需要更多的工作,而不是僅僅在部署運行中減少文件文件的好處。我也試過嵌入,動態地加載程序集,這幫助我更多地理解幕後的東西,但它不適用於它。我在最早的時候加載了程序集,但是有很多靜態類訪問它們,並且需要它們重新加載,但即使是靜態構造函數也太遲了,很奇怪。總是失敗。你的答案有最多的背景,所以謝謝! – 2013-05-08 11:04:04

+0

你是什麼意思的'在最早點加載程序集'?我已經成功地使用Jeffrey Richter在他的博客中描述的方法(請參閱我之前的評論)。這是非常直接的,唯一值得注意的是,你需要確保你的AssemblyResolve事件在其他任何事情之前被初始化,在我的情況下,這意味着創建一個新的入口點來設置AssemblyResolve事件,然後調用舊的入口點。 – sgmoore 2013-05-08 12:43:00

+0

@RomanMittermayr:我也很疑惑「最早」的一點。雖然靜態構造函數相當早,但earlies點是模塊初始化器。不幸的是,模塊初始化器不能用C#修改,但可以通過一些較小的IL操作進行修改(請參閱:https://github.com/Fody/ModuleInit)。當你說「重新加載」時,我感覺Quartz.NET正在創建單獨的AppDomain。事實上,這可能是一個問題,如果您無法訪問它們並將AssemblyResolver注入到它們中。你也可以嘗試https://libz.codeplex.com/。 – 2013-05-08 13:20:04

1

您可以嘗試創建自己的ISchedulerFactory,並避免使用反射來加載所有類型。 StdSchedulerFactory使用此代碼來創建線程池。這是您的錯誤發生,將開始尋找更改的地方:

 Type tpType = loadHelper.LoadType(cfg.GetStringProperty(PropertyThreadPoolType)) ?? typeof(SimpleThreadPool); 

     try 
     { 
      tp = ObjectUtils.InstantiateType<IThreadPool>(tpType); 
     } 
     catch (Exception e) 
     { 
      initException = new SchedulerException("ThreadPool type '{0}' could not be instantiated.".FormatInvariant(tpType), e); 
      throw initException; 
     } 

被調用的方法ObjectUtils.InstantiateType這是一個和最後一行是一個扔你的異常:

public static T InstantiateType<T>(Type type) 
    { 
     if (type == null) 
     { 
      throw new ArgumentNullException("type", "Cannot instantiate null"); 
     } 
     ConstructorInfo ci = type.GetConstructor(Type.EmptyTypes); 
     if (ci == null) 
     { 
      throw new ArgumentException("Cannot instantiate type which has no empty constructor", type.Name); 
     } 
     return (T) ci.Invoke(new object[0]); 
    } 

在工廠的這一部分之後,數據源使用相同的模式加載,然後作業本身也被動態加載,這意味着您還必須編寫自己的JobFactory。由於Quartz.Net會在運行時動態地加載一堆零碎的東西,這意味着您最終可能會重寫相當多的東西。

相關問題