最近,我的任務(wù)是編寫一個數(shù)據(jù)提取/報告框架,該框架將使客戶端應(yīng)用程序能夠定義要執(zhí)行的存儲過程,并將結(jié)果通過電子郵件發(fā)送給感興趣的各方。使該項目變得有趣的是允許用戶從一組查找表,Web服務(wù)數(shù)據(jù)和應(yīng)用程序派生數(shù)據(jù)中指定參數(shù)值的要求。在沒有任何外部引用的情況下,檢索Web服務(wù)或應(yīng)用程序派生數(shù)據(jù)的要求需要一種機制來執(zhí)行未引用程序集中的方法。 本文概述了我的方法,該方法允許框架代碼通過部分類型名解析來執(zhí)行未引用程序集中的方法。 問題查找表數(shù)據(jù)的預(yù)填充非常簡單-從表中選擇Id和Value字段-這是通過一個簡單的ParameterLookup表實現(xiàn)的。但是,當(dāng)有時間用Web服務(wù)數(shù)據(jù)或特定于應(yīng)用程序的數(shù)據(jù)預(yù)先填充查找時,我們需要某種機制來執(zhí)行客戶端應(yīng)用程序中的方法。在沒有對客戶端應(yīng)用程序的任何引用的情況下,我們需要一種機制來解析和執(zhí)行客戶端應(yīng)用程序代碼。我們實現(xiàn)的方法是LookupClass在ParameterLookup表中包含一個字段,并讓框架代碼解析類型并執(zhí)行該search方法。 有許多常用的方法允許.NET代碼解析和執(zhí)行未引用類型的方法,盡管并非所有方法都適用于所有環(huán)境。當(dāng)宿主進程是COM+進程時,Type.GetType()可以將調(diào)用與部分限定的類型名稱(即MyCompany.MyApplication.Search.Lookup.SomeLookup,MyCompany.MyApplication)一起使用。但是,當(dāng)主機進程是IIS時,必須指定完全限定的類型名稱(“類型名稱”,“程序集名稱”,“版本”,“區(qū)域性”,“公鑰”),或者該程序集必須存在于Web程序包的/bin目錄中。 不希望ParameterLookup每次執(zhí)行新版本的應(yīng)用程序時都強制客戶端應(yīng)用程序更新表,我們需要一種僅使用Type和Assembly名稱部分的機制。 解決方案如果您沒有完全限定的類型名稱(類型名稱,程序集類型,版本,區(qū)域性和公共密鑰),則在未引用的程序集中定義的類型可以是正確的PITA。 例如,類型Microsoft.Build.BuildEngine.Engine類型的完全限定名稱為Microsoft.Build.BuildEngine.Engine,Microsoft.Build.Engine,Culture=neutral,Version=4.0.0.0,PublicKeyToken=b03f5f7f11d50a3a(真是令人討厭!)。對于相對穩(wěn)定的組件,可以在配置表/文件中指定完全限定的類型名稱,但是對于版本號更改為相對不穩(wěn)定的不穩(wěn)定組件(例如,來自客戶端應(yīng)用程序的組件),您該怎么做?每個版本? Type.GetType() 該Type.GetType()功能可用于加載在項目中引用或在程序集搜索路徑中的程序集。 Type t = Type.GetType("MyCompany.MyApplication.Lookup.SomeLookup, MyCompany.MyApplication "); 1復(fù)制代碼類型:[html] 由于框架代碼不會引用該程序集,因此我們唯一的希望是該程序集位于程序集搜索路徑中。盡管這種情況是可能的,但肯定不能保證。 該解決方案必須丟棄。 如果我們?yōu)門ype.GetType()函數(shù)提供完全限定的類型名稱該怎么辦? Type t = Type.GetType("MyCompany.MyApplication.Lookup.SomeLookup, MyCompany.MyApplication, Culture=neutral, Version=1.0.0.3245, PublicKey= 1e9a8d893e3afa78");12復(fù)制代碼類型:[html] 此調(diào)用肯定有效,并且類型引用已成功返回。所需要做的就是將此完全合格的類型傳遞給框架代碼-相對簡單的練習(xí)。las,當(dāng)重新編譯程序集時,將使用不同的內(nèi)部版本號(假定您使用的是內(nèi)部版本號)創(chuàng)建該組件的新版本,該調(diào)用將返回原始程序集;否則,將返回原始程序集。不是更新的程序集。這是不理想的。 該解決方案必須丟棄。 我需要的是一種無需指定版本號或無需依靠程序集搜索路徑中的程序集即可解決類型的方法。 Assembly.LoadFrom()該Assembly.LoadFrom()方法看起來很有希望,因為我可以使用此函數(shù)來加載程序集而無論其位置如何,并遍歷該程序集中定義的每種類型。 internal static Type GetTypeFromAssembly(string assemblyName, string typeName) { Assembly assembly = Assembly.LoadFrom(gacPath); Type[] types = assembly.GetTypes(); foreach (Type type in types) { if (type.FullName == typeName)) return type; } return null; }123456789101112復(fù)制代碼類型:[html] 條件if(type.FullName==typeName)使我們不必指定完全限定類型的版本,區(qū)域性或公鑰屬性?,F(xiàn)在,我們可以加載程序集并提取適當(dāng)?shù)念愋鸵怨┦褂谩?/p> 但是,這導(dǎo)致了代碼和安裝實現(xiàn)之間的依賴性。如果支持操作員決定修改應(yīng)用程序安裝參數(shù),則可以將軟件安裝到未知位置??梢韵胂?,我們可能要求將軟件安裝到特定位置(例如ProgramFiles),但這會導(dǎo)致32位/64位問題(ProgramFiles是指“ProgramFiles”還是“ProgramFiles(x86)”?)。 但我想我快到了。 融合如果將擁有我們所要類型的程序集安裝到全局程序集緩存中,則我們可以利用CLR程序集管理系統(tǒng)為我們加載并找到我們的程序集。 使用Fusion.dll程序集(有關(guān)詳細信息,請參閱此鏈接),我們可以創(chuàng)建對適當(dāng)?shù)腉AC(32位或64位)的引用,并使用該Assembly.LoadFrom()方法安全地加載我們的程序集。 [DllImport("fusion.dll")] internal static extern int CreateAssemblyCache(out IAssemblyCache ppAsmCache, int reserved);12復(fù)制代碼類型:[html] private static string GetAssemblyPath(string name) { if (name == null) throw new ArgumentNullException("name"); string finalName = name; AssemblyInfo aInfo = new AssemblyInfo(); aInfo.cchBuf = 1024; // should be fine... aInfo.currentAssemblyPath = new String('\0', aInfo.cchBuf); IAssemblyCache ac; int hr = CreateAssemblyCache(out ac, 0); if (hr >= 0) { hr = ac.QueryAssemblyInfo(0, finalName, ref aInfo); if (hr < 0) return null; } return aInfo.currentAssemblyPath; }123456789101112131415161718192021復(fù)制代碼類型:[html] public static Type ResolveType(string assemblyName, string typeName) { string gacPath = GetAssemblyPath(assemblyName); return GetTypeFromAssembly(assemblyName, typeName); }12345復(fù)制代碼類型:[html] 僅使用類型和程序集的名稱,代碼將搜索適合于應(yīng)用程序環(huán)境(x86/x64)的GAC目錄,并返回對該類型的引用。引用正確的類型后,對的調(diào)用Activator.CreateInstance()將創(chuàng)建要使用的可用類型。 使用代碼框架定義了要解析的類型必須實現(xiàn)的接口。 namespace FrameworkApp { public interface ILookup { string Search(); } }1234567復(fù)制代碼類型:[html] ...當(dāng)類實現(xiàn)接口時。 public class SearchType1 : ILookup { public string Search() { return string.Format("SearchType1.Search() from version {0}", Assembly.GetExecutingAssembly().GetName().Version); } }12345678復(fù)制代碼類型:[html] 現(xiàn)在,我們只需要調(diào)用TypeResolver.ResolveType(),創(chuàng)建一個返回類型的實例(強制進入選擇的接口),最后執(zhí)行所需的方法。 Type type = TypeResolver.ResolveType("ClientApp.SearchType1,ClientApp"); ILookup lookup = Activator.CreateInstance(type) as ILookup; lookup.Search();123復(fù)制代碼類型:[html] 局限性 程序集必須在GAC中注冊才能使此解決方案生效 不會加載不兼容的.NET版本程序集 將解析為最高版本,而不是最新編譯的版本 |
|
來自: 碼農(nóng)9527 > 《WEB》