|
一、前言 插件模型指應(yīng)用程序由一些動(dòng)態(tài)的獨(dú)立模塊構(gòu)成,每個(gè)模塊均具有一個(gè)或多個(gè)服務(wù),并滿足一定的插件協(xié)議,能夠借助主程序?qū)崿F(xiàn)主程序-插件,插件-插件之間的通訊。它定義了一套公共的接口,通過(guò)接口與插件進(jìn)行通信,主要是通過(guò)反射來(lái)獲取相關(guān)的屬性和方法,然后再執(zhí)行指定的操作。其實(shí),它也可以理解為定義一套通用的解決方案,通過(guò)反射來(lái)獲取相應(yīng)的程序集的相關(guān)類(lèi)型,然后執(zhí)行這些指定類(lèi)型的相關(guān)操作。它是一種即插即用的方案,更新及維護(hù)簡(jiǎn)便。 本文僅僅是描述插件開(kāi)發(fā)的大體模型,設(shè)計(jì)比較簡(jiǎn)單,主要的步驟如下: (1)、定義公共的接口以及抽象類(lèi)。 (2)、定義和實(shí)現(xiàn)相關(guān)組件。 (3)、實(shí)現(xiàn)通用程序集反射操作類(lèi)。
其中,公共的接口和抽象類(lèi)定義在組件Jasen.Framework.Core中,該組件中提供通用程序集反射操作類(lèi)AssemblyUtility;具體實(shí)現(xiàn)的相關(guān)組件為Jasen.Framework.Oracle、Jasen.Framework.Access和Jasen.Framework.SqlServer,它們都實(shí)現(xiàn)了Jasen.Framework.Core中的公共接口。客戶(hù)端可以根據(jù)實(shí)際情況來(lái)進(jìn)行相應(yīng)的操作。相關(guān)組件圖如下:
二、公共接口和抽象類(lèi)的定義以及相關(guān)組件的定義和實(shí)現(xiàn) 首先,定義公共的接口以及抽象類(lèi),如下類(lèi)圖所示,定義一個(gè)公共的接口IDataTable,定義一個(gè)抽象類(lèi)DataTable,這些公共的類(lèi)型放置在最頂端的程序集中。而其他組件將分別重新創(chuàng)建,實(shí)現(xiàn)相對(duì)應(yīng)的功能,如SqlServerDataTable、OracleDataTable和AccessDataTable實(shí)現(xiàn)各自的功能。注意:Assembly.LoadFile(file)動(dòng)態(tài)加載程序集時(shí),該程序集在當(dāng)前的運(yùn)行環(huán)境中必須不存在的,否則可能會(huì)出現(xiàn)意想不到的數(shù)據(jù)異常,因此相關(guān)組件的實(shí)現(xiàn)必須是獨(dú)立的(僅僅是實(shí)現(xiàn)公共的接口)。
三、通用程序集反射操作類(lèi)的實(shí)現(xiàn) 下面的AssemblyUtility主要是對(duì)程序集操作的通用類(lèi),可以根據(jù)指定目錄以及文件列表動(dòng)態(tài)獲取相應(yīng)的程序集。同時(shí),也可以通過(guò)目錄,文件以及程序集獲取相關(guān)的類(lèi)型集合和對(duì)象集合。其中需要注意的是,實(shí)現(xiàn)的子類(lèi)必須提供默認(rèn)構(gòu)造函數(shù)??蛻?hù)端可以通過(guò)該類(lèi)獲取相應(yīng)的類(lèi)型和對(duì)象集合,然后再執(zhí)行相應(yīng)的操作。這些操作都是通過(guò)動(dòng)態(tài)加載程序集來(lái)實(shí)現(xiàn)的,代碼如下所示: public static class AssemblyUtility { public static IEnumerable<Type> GetImplementdTypesByDirectory<T>(string baseDirectory) { IList<Assembly> assemblies= GetAssemblies(baseDirectory); List<Type> types = new List<Type>(); foreach (Assembly assembly in assemblies) { types.AddRange(GetImplementdTypes<T>(assembly)); } return types; } public static IEnumerable<Type> GetImplementdTypes<T>(string assemblyFile) { if (!File.Exists(assemblyFile)) { return null; } try { return GetImplementdTypes<T>(Assembly.LoadFile(assemblyFile)); } catch (Exception ex) { return null; } } public static IEnumerable<Type> GetImplementdTypes<T>(Assembly assembly) { if (assembly == null) { return null; } return assembly.GetExportedTypes().Where(p => p.IsSubclassOf(typeof(T)) && (!p.IsAbstract) && (!p.IsInterface)); } public static IList<T> GetImplementedObjectsByDirectory<T>(string baseDirectory) { IList<Assembly> assemblies = GetAssemblies(baseDirectory); List<T> entities = new List<T>(); foreach (Assembly assembly in assemblies) { entities.AddRange(GetImplementedObjects<T>(assembly)); } return entities; } public static IList<T> GetImplementedObjects<T>(string assemblyFile) { if (!File.Exists(assemblyFile)) { return null; } try { return GetImplementedObjects<T>(Assembly.LoadFile(assemblyFile)); } catch (Exception ex) { return null; } } public static IList<T> GetImplementedObjects<T>(Assembly assembly) { if (assembly == null) { return null; } IEnumerable<Type> types = GetImplementdTypes<T>(assembly); var result = new List<T>(); foreach (Type type in types) { ConstructorInfo constructor = type.GetConstructor(new Type[0]); if (constructor == null) { continue; } object instance = Activator.CreateInstance(type); if (instance is T) { result.Add((T)instance); } } return result; } public static IList<Assembly> GetAssemblies(string baseDirectory) { if (!Directory.Exists(baseDirectory)) { return new List<Assembly>(); } string[] files = Directory.GetFiles(baseDirectory, "*.dll"); return GetAssemblies(files); } public static IList<Assembly> GetAssemblies(string[] assemblyFiles) { IList<Assembly> assemblies = new List<Assembly>(); try { foreach (string file in assemblyFiles) { if (!File.Exists(file)||(!file.EndsWith(".dll",StringComparison.InvariantCultureIgnoreCase))) { continue; } assemblies.Add(Assembly.LoadFile(file)); } } catch (Exception ex) { return new List<Assembly>(); } return assemblies; } }
public static IEnumerable<Type> GetImplementdTypesByDirectory<T>(string baseDirectory) 以上3個(gè)方法根據(jù)不同的參數(shù)(目錄、地址、程序集)來(lái)動(dòng)態(tài)獲取程序集中的特定類(lèi)型集合,這些類(lèi)型為類(lèi)型T的類(lèi)或者子類(lèi)(非抽象類(lèi)和接口)。
public static IList<T> GetImplementedObjectsByDirectory<T>(string baseDirectory) ConstructorInfo constructor = type.GetConstructor(new Type[0]); 四、通用程序集反射操作類(lèi)的單元測(cè)試 AssemblyUtility類(lèi)主要的單元測(cè)試如下,僅驗(yàn)證了正確的情況,代碼如下: public class AssemblyUtilityTest { [TestMethod()] public void GetAssembliesTest() { string assemblyPath = AppDomain.CurrentDomain.BaseDirectory+"\\Files\\"; IList<Assembly> result = AssemblyUtility.GetAssemblies(assemblyPath); Assert.IsNotNull(result); Assert.AreEqual(3, result.Count); } [TestMethod()] public void GetAssembliesByFilesTest() { string[] assemblyFiles = new string[] { AppDomain.CurrentDomain.BaseDirectory + "\\Jasen.Framework.Core.dll", AppDomain.CurrentDomain.BaseDirectory + "\\Jasen.Framework.Core.Test.dll", "www", "ww.dll"}; IList<Assembly> result = AssemblyUtility.GetAssemblies(assemblyFiles); Assert.IsNotNull(result); Assert.AreEqual(2, result.Count); } [TestMethod()] public void GetImplementedObjectsByDirectoryTest() { string assemblyDir = AppDomain.CurrentDomain.BaseDirectory + "\\Files\\"; IList<DataTable> result = AssemblyUtility.GetImplementedObjectsByDirectory<DataTable>(assemblyDir); Assert.IsNotNull(result); Assert.AreEqual(3, result.Count); } [TestMethod()] public void GetImplementedObjectsTest() { string assemblyFile =AppDomain.CurrentDomain.BaseDirectory + "\\Files\\Jasen.Framework.Oracle.dll"; IList<DataTable> result = AssemblyUtility.GetImplementedObjects<DataTable>(assemblyFile); Assert.IsNotNull(result); Assert.AreEqual(1, result.Count); } [TestMethod()] public void GetImplementedTypesTest() { string assemblyFile = AppDomain.CurrentDomain.BaseDirectory + "\\Files\\Jasen.Framework.Oracle.dll"; IEnumerable<Type> types = AssemblyUtility.GetImplementdTypes<DataTable>(assemblyFile); Assert.IsNotNull(types); int count = 0; foreach (var type in types) { Assert.IsTrue(type.IsSubclassOf(typeof(DataTable))); Assert.IsFalse(type.IsAbstract); Assert.IsFalse(type.IsInterface); count++; } Assert.AreEqual(1, count); } [TestMethod()] public void GetImplementdTypesByDirectoryTest() { string assemblyDir = AppDomain.CurrentDomain.BaseDirectory + "\\Files\\"; IEnumerable<Type> types = AssemblyUtility.GetImplementdTypesByDirectory<DataTable>(assemblyDir); Assert.IsNotNull(types); int count = 0; foreach (var type in types) { Assert.IsTrue(type.IsSubclassOf(typeof(DataTable))); Assert.IsFalse(type.IsAbstract); Assert.IsFalse(type.IsInterface); count++; } Assert.AreEqual(3, count); } }
五、總結(jié) 全文中主要圍繞AssemblyUtility通用類(lèi)來(lái)進(jìn)行講解的,僅僅是插件開(kāi)發(fā)的一個(gè)思路。具體應(yīng)用的話,應(yīng)該相對(duì)來(lái)說(shuō)比較直接,在客戶(hù)端獲取相應(yīng)的類(lèi)型集合以及對(duì)象集合,然后再執(zhí)行這些集合的具體操作即可。其中,實(shí)現(xiàn)的組件(插件)放置在指定的目錄下,通過(guò)AssemblyUtility類(lèi)即可動(dòng)態(tài)加載目錄下的程序集,從而獲取到指定類(lèi)型的數(shù)據(jù)。具體執(zhí)行什么操作,實(shí)現(xiàn)什么功能,這些都是在組件(插件)中實(shí)現(xiàn)即可。
源代碼下載:C#插件開(kāi)發(fā)模型源代碼 |
|
|
來(lái)自: ThinkTank_引擎 > 《插件化開(kāi)發(fā)》