將其類型謂詞請閱讀整個問題。我有一個獨特的情況,有幾個我想解決的限制。加載一個組件,而在另一個應用程序域
在我的代碼有被編譯成Predicate<System.Type>
表達式樹。我的目標是加載程序集沒有鎖定它(這是該項目的輸出組件,被不斷地重修),適用其類型的列表在這個謂語並獲得結果類型的回列表名:
// this is what I need:
return assembly.GetTypes().Where(t => predicate(t)).Select(t => t.FullName);
該程序集應該加載到另一個應用程序域中,因爲我希望一收到我需要的信息就立即卸載它。
這是它得到棘手。有幾個我正面臨的問題:
如果我在另一個appdomain中加載程序集並簡單地返回它的所有類型的數組,以便我可以將謂詞應用回到我的主appdomain中,只要類型被編組回到我的主要應用程序域我得到一個FileNotFoundException
,指出該組件是找不到的。這使得sence,因爲程序集只被加載到我創建的另一個appdomain中。在主應用程序域中加載它也會失敗。
如果或者我嘗試將謂詞傳遞給另一個appdomain並將其應用於其中並返回一個字符串數組(完整類型名稱),我得到一個SerializationException: "Cannot serialize delegates over unmanaged function pointers, dynamic methods or methods outside the delegate creator's assembly."
,因爲謂詞是一個動態方法(已編譯從表達式樹中)。
將其加載到主應用程序域中將不會有這些問題,但由於無法卸載已加載的程序集而無需卸載整個應用程序域,只要程序集發生更改(重建後),任何嘗試加載程序集具有相同的名稱將導致例外。
語境:
我建立ReSharper的一個插件叫做Agent Mulder。插件背後的想法是分析解決方案中的DI/IoC容器註冊,並幫助ReSharper找出通過DI容器註冊的類型的使用情況(您可以觀看它的工作原理的簡短視頻here)。
大多數情況下,分析容器註冊非常簡單 - 我只需檢測足夠的信息即可知道哪些具體類型受到影響。在Castle Windsor的這個例子中:Component.For<IFoo>().ImplementedBy<Foo>()
由此產生的類型是顯而易見的,AllTypes.FromThisAssembly().BasedOn<IFoo>()
也是如此 - 給我提供足夠的信息來對這條線會影響到的具體類型提供猜測。然而,考慮這個註冊在溫莎城堡:
container.Register(Classes
.FromAssemblyInDirectory(new AssemblyFilter(".").FilterByName(an => an.Name.StartsWith("Ploeh.Samples.Booking")))
.Where(t => !(t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Dispatcher<>)))
.WithServiceAllInterfaces());
(source)
這裏的信息取決於只會在運行時進行評估的謂詞。
因爲我所能做的就是靜態分析這一點,所以我手裏拿着來自Where
子句的lambda表達式的ReSharper AST(在ReSharper中稱爲PSI)表示。我可以將這個AST轉換成一個LINQ表達式樹,然後將它編譯成一個委託。
我的想法是是加載通過反射輸出組件(由FromAssembly*
描述符確定),並且爲了獲得類型名(我只需要姓名)在組裝的類型應用此委託。每次裝配改變時都必須重新評估(我不關心性能的這一點)。總之,除非有人能夠推薦一種更好的方式來確定受謂詞影響的類型,我想知道如何用反射來做到這一點(不幸的是我沒有考慮過其他元數據讀者,因爲我會必須以某種方式將lambda表達式AST轉換爲不同數據類型的謂詞,並且我不知道是否存在1對1轉換)。
謝謝您的閱讀。這個問題在可用時將有500點獎勵。
如果'Expression'是序列化,你可以創建'Predicate'委託作爲一個'Expression',並將其傳遞給appdomain邊界,然後在'remote'appdomain中進行編譯。但可悲的是,那不是那種情況:( – leppie
@leppie是的,我想過序列化表達式並傳遞它,你是對的,它並不像聽起來那麼簡單... –