在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
插件模型指应用程序由一些动态的独立模块构成,每个模块均具有一个或多个服务,并满足一定的插件协议,能够借助主程序实现主程序-插件,插件-插件之间的通讯。它定义了一套公共的接口,通过接口与插件进行通信,主要是通过反射来获取相关的属性和方法,然后再执行指定的操作。其实,它也可以理解为定义一套通用的解决方案,通过反射来获取相应的程序集的相关类型,然后执行这些指定类型的相关操作。它是一种即插即用的方案,更新及维护简便。 本文仅仅是描述插件开发的大体模型,设计比较简单,主要的步骤如下: (1)、定义公共的接口以及抽象类。 (2)、定义和实现相关组件。 (3)、实现通用程序集反射操作类。
其中,公共的接口和抽象类定义在组件Jasen.Framework.Core中,该组件中提供通用程序集反射操作类AssemblyUtility;具体实现的相关组件为Jasen.Framework.Oracle、Jasen.Framework.Access和Jasen.Framework.SqlServer,它们都实现了Jasen.Framework.Core中的公共接口。客户端可以根据实际情况来进行相应的操作。相关组件图如下:
二、公共接口和抽象类的定义以及相关组件的定义和实现 首先,定义公共的接口以及抽象类,如下类图所示,定义一个公共的接口IDataTable,定义一个抽象类DataTable,这些公共的类型放置在最顶端的程序集中。而其他组件将分别重新创建,实现相对应的功能,如SqlServerDataTable、OracleDataTable和AccessDataTable实现各自的功能。注意:Assembly.LoadFile(file)动态加载程序集时,该程序集在当前的运行环境中必须不存在的,否则可能会出现意想不到的数据异常,因此相关组件的实现必须是独立的(仅仅是实现公共的接口)。
三、通用程序集反射操作类的实现 下面的AssemblyUtility主要是对程序集操作的通用类,可以根据指定目录以及文件列表动态获取相应的程序集。同时,也可以通过目录,文件以及程序集获取相关的类型集合和对象集合。其中需要注意的是,实现的子类必须提供默认构造函数。客户端可以通过该类获取相应的类型和对象集合,然后再执行相应的操作。这些操作都是通过动态加载程序集来实现的,代码如下所示:
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个方法根据不同的参数(目录、地址、程序集)来动态获取程序集中的特定类型集合,这些类型为类型T的类或者子类(非抽象类和接口)。
public static IList<T> GetImplementedObjectsByDirectory<T>(string baseDirectory) ConstructorInfo constructor = type.GetConstructor(new Type[0]); 四、通用程序集反射操作类的单元测试 AssemblyUtility类主要的单元测试如下,仅验证了正确的情况,代码如下:
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); } }
五、总结 全文中主要围绕AssemblyUtility通用类来进行讲解的,仅仅是插件开发的一个思路。具体应用的话,应该相对来说比较直接,在客户端获取相应的类型集合以及对象集合,然后再执行这些集合的具体操作即可。其中,实现的组件(插件)放置在指定的目录下,通过AssemblyUtility类即可动态加载目录下的程序集,从而获取到指定类型的数据。具体执行什么操作,实现什么功能,这些都是在组件(插件)中实现即可。
源代码下载:C#插件开发模型源代码 |
请发表评论