C#插件开发简单模型

一、前言 

插件模型指应用程序由一些动态的独立模块构成,每个模块均具有一个或多个服务,并满足一定的插件协议,能够借助主程序实现主程序-插件,插件-插件之间的通讯。它定义了一套公共的接口,通过接口与插件进行通信,主要是通过反射来获取相关的属性和方法,然后再执行指定的操作。其实,它也可以理解为定义一套通用的解决方案,通过反射来获取相应的程序集的相关类型,然后执行这些指定类型的相关操作。它是一种即插即用的方案,更新及维护简便。

本文仅仅是描述插件开发的大体模型,设计比较简单,主要的步骤如下:

(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)
public static IEnumerable<Type> GetImplementdTypes<T>(string assemblyFile)
public static IList<T> GetImplementedObjects<T>(Assembly assembly)

以上3个方法根据不同的参数(目录、地址、程序集)来动态获取程序集中的特定类型集合,这些类型为类型T的类或者子类(非抽象类和接口)。

 

public static IList<T> GetImplementedObjectsByDirectory<T>(string baseDirectory)
public static IList<T> GetImplementedObjects<T>(string assemblyFile)
public static IList<T> GetImplementedObjects<T>(Assembly assembly)
而以上3个方法根据不同的参数(目录、地址、程序集)来动态获取程序集中的特定对象集合,这些对象为类型T的类或者子类(非抽象类和接口)的实例。当组件中子类存在有参构造函数时,必须实现默认构造函数。从如下代码可以看出:如果默认构造函数不存在,将不会添加该对象实例。

                ConstructorInfo constructor = type.GetConstructor(new Type[0]);
                
if (constructor == null)
                {
                    
continue;
                }

                
object instance = Activator.CreateInstance(type);
                
if (instance is T)
                {
                    result.Add((T)instance);
                }

四、通用程序集反射操作类的单元测试

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#插件开发模型源代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值