Work.Dynamic.Loaddll.Note
今天做动态加载dll发现一个好的泛型读取列的方法,结合自己的使用,还有自己的命名的越来越感觉好一些,看代码就像读英文。
DllFile.cs.Code
1 /// <summary> 2 /// dll文件 3 /// </summary> 4 internal struct DllFile 5 { 6 /// <summary> 7 /// 文件名 8 /// </summary> 9 public string Name { get; set; } 10 11 /// <summary> 12 /// 二进制文件 13 /// </summary> 14 public byte[] Content { get; set; } 15 16 /// <summary> 17 /// 描述 18 /// </summary> 19 public string Comment { get; set; } 20 21 /// <summary> 22 /// 上传用户id 23 /// </summary> 24 public string OwnerId { get; set; } 25 26 /// <summary> 27 /// 上传时间 28 /// </summary> 29 public DateTime UploadTime { get; set; } 30 }
DataRow.ReadColumn.Code.Generic (泛型读取列.感觉还不错)
1 private static T GetColumnValue<T>(DataRow dr, string name) 2 { 3 try 4 { 5 if (dr[name] == DBNull.Value) 6 { 7 return GetDefault<T>(); 8 } 9 else 10 { 11 return (T)dr[name]; 12 } 13 }//如果不存在这一列 14 catch (ArgumentException ex) 15 { 16 throw new ArgumentException(string.Format("{0}列不存在。", name)); 17 } 18 } 19 private const string emptyStr = ""; 20 /// <summary> 21 /// 根据类型获取默认值 22 /// </summary> 23 /// <typeparam name="T"></typeparam> 24 /// <returns></returns> 25 private static T GetDefault<T>() 26 { 27 T t = default(T); 28 if (emptyStr is T) 29 return (T)(object)emptyStr; 30 return t; 31 }
结合自己的代码读取列
Read.DllFile from DataRow.Code
1 protected ICollection<DllFile> GetDllFiles(DataSet ds) 2 { 3 ICollection<DllFile> result = new Collection<DllFile>(); 4 if (null == ds) 5 throw new ArgumentNullException("ds"); 6 int rCount = ds.Tables[0].Rows.Count; 7 if (rCount > 0) 8 { 9 for (int i = 0; i < rCount; i++) 10 { 11 DataRow dr = ds.Tables[0].Rows[i]; 12 result.Add(FormatDllFile(dr)); 13 } 14 } 15 return result; 16 } 17 18 private DllFile FormatDllFile(DataRow dr) 19 { 20 DllFile file = new DllFile(); 21 file.OwnerId = GetColumnValue<string>(dr, userField); 22 file.Name = GetColumnValue<string>(dr, fileNameField); 23 file.Content = GetColumnValue<byte[]>(dr, fileContentField); 24 file.Comment = GetColumnValue<string>(dr, commentField); 25 file.UploadTime = GetColumnValue<DateTime>(dr, updateTimeField); 26 return file; 27 } 28 /// <summary> 29 /// 根据类型或DataRow列 30 /// </summary> 31 /// <typeparam name="T"></typeparam> 32 /// <param name="dr"></param> 33 /// <param name="name"></param> 34 /// <returns></returns> 35 private static T GetColumnValue<T>(DataRow dr, string name) 36 { 37 try 38 { 39 if (dr[name] == DBNull.Value) 40 { 41 return GetDefault<T>(); 42 } 43 else 44 { 45 return (T)dr[name]; 46 } 47 }//如果不存在这一列 48 catch (ArgumentException ex) 49 { 50 throw new ArgumentException(string.Format("{0}列不存在。", name)); 51 } 52 } 53 private const string emptyStr = ""; 54 /// <summary> 55 /// 根据类型获取默认值 56 /// </summary> 57 /// <typeparam name="T"></typeparam> 58 /// <returns></returns> 59 private static T GetDefault<T>() 60 { 61 T t = default(T); 62 if (emptyStr is T) 63 return (T)(object)emptyStr; 64 return t; 65 }
设计通过接口和反射来实现程序模块之间的解耦关系的一种机制,实现者将接口的实现类的Type和标记注册的程序集中,程序启动,或者用户更换dll到数据库时,更新全局静态注册字典,将类型更新到字典中。
使用者根据与实现者沟通好的标记,创建接口实例,最终可以在不同的模块调用接口方法。
程序集属性类
Assembly.ExtensionAttribute.Code
1 public class RegisterImplAttribute : Attribute 2 { 3 public Type InterfaceType { get; private set; } 4 5 public string ImplFlag { get; private set; } 6 7 public Type ImplType { get; private set; } 8 9 public RegisterImplAttribute(Type implType, string implFlag) 10 { 11 this.ImplType = implType; 12 this.ImplFlag = implFlag; 13 } 14 15 public RegisterImplAttribute(Type iType, Type implType, string implFlag) 16 { 17 this.InterfaceType = iType; 18 this.ImplType = implType; 19 this.ImplFlag = implFlag; 20 } 21 }
重要的一步在程序集中添加接口实现类和实现类的标记,打开工程下.Properties/Assemboy.cs
1 在文件中添加程序集属性。 2 [assembly: Dynamic.RegisterImpl(typeof(AppendixFileController), "CRCC.Appendixs.1")] 3 "CRCC.Appendixs.1"是接口实现类的标记,用于与其他接口实现类区分。通过这个标记最终才能找到实现类的。
设计一个静态服务类,用于保存dll,同时动态加载程序集和程序集中的接口实现类,最终提供给用于一个接口实例。
DynamicDllService.Code
1 public static class DynamicDllService 2 { 3 private static IDictionary<string, Type> IInstances = new Dictionary<string, Type>(); 4 /// <summary> 5 /// 动态dll存放目录 6 /// </summary> 7 private static readonly string folderDir = System.AppDomain.CurrentDomain.BaseDirectory + "\\DyBin"; 8 9 /// <summary> 10 /// 加载所有动态dll表中的dll、并注册 11 /// </summary> 12 public static void InitAllInstance(IContext context) 13 { 14 DynamicDllDao dllDao = new DynamicDllDao(context); 15 ICollection<DllFile> files = dllDao.SelectAll(); 16 if (null != files && files.Count > 0) 17 { 18 DllStreamLocalize dllReader = new DllStreamLocalize(); 19 foreach (var file in files) 20 { 21 dllReader.SaveFileToLocal(file.Content, folderDir, file.Name); 22 string filePath = string.Format("{0}{1}", dllReader.VerifyPath(folderDir), dllReader.GetDllFullName(file.Name)); 23 RegisterDllImpl(filePath); 24 } 25 } 26 } 27 28 /// <summary> 29 /// 保存dll并注册到系统中 30 /// </summary> 31 /// <param name="context">上下文</param> 32 /// <param name="userId">用户id</param> 33 /// <param name="fileName">文件名</param> 34 /// <param name="filebit">二进制文件</param> 35 /// <param name="comment">文件描述</param> 36 public static void SaveFile(IContext context, string userId, string fileName, byte[] filebit, string comment) 37 { 38 if (null == context) 39 throw new ArgumentNullException("context"); 40 if (string.IsNullOrEmpty(fileName)) 41 throw new ArgumentNullException("fileName"); 42 if (string.IsNullOrEmpty(userId)) 43 throw new ArgumentNullException("userId"); 44 DynamicDllDao dllDao = new DynamicDllDao(context); 45 DllFile? file = dllDao.SearchByFileName(fileName); 46 if (file != null) 47 dllDao.Update(userId, fileName, filebit, comment); 48 else 49 dllDao.Insert(userId, fileName, filebit, comment); 50 51 //实例化到本地 52 DllStreamLocalize dllReader = new DllStreamLocalize(); 53 dllReader.SaveFileToLocal(filebit, folderDir, fileName); 54 string filePath = string.Format("{0}{1}", dllReader.VerifyPath(folderDir), dllReader.GetDllFullName(fileName)); 55 RegisterDllImpl(filePath); 56 } 57 58 private static void RegisterDllImpl(string filePath) 59 { 60 object o = new object(); 61 lock (o) 62 { 63 IDictionary<string, Type> implTypes = GetInstanceTypes(filePath); 64 foreach (var item in implTypes) 65 { 66 if (IInstances.ContainsKey(item.Key)) 67 IInstances[item.Key] = item.Value; 68 else 69 IInstances.Add(item); 70 } 71 } 72 } 73 74 private static IDictionary<string, Type> GetInstanceTypes(string filePath) 75 { 76 if (string.IsNullOrEmpty(filePath)) 77 throw new ArgumentNullException("filePath"); 78 79 if (!File.Exists(filePath)) 80 throw new FileNotFoundException(string.Format("加载:{0},失败。", filePath)); 81 Assembly assmbly = Assembly.LoadFile(filePath); 82 object[] assAttrs = assmbly.GetCustomAttributes(typeof(RegisterImplAttribute), false); 83 84 IDictionary<string, Type> result = new Dictionary<string, Type>(); 85 if (assAttrs.Length > 0) 86 { 87 for (int i = 0; i < assAttrs.Length; i++) 88 { 89 RegisterImplAttribute reg = assAttrs[i] as RegisterImplAttribute; 90 if (result.ContainsKey(reg.ImplFlag)) 91 result[reg.ImplFlag] = reg.ImplType; 92 else 93 result.Add(reg.ImplFlag, reg.ImplType); 94 } 95 } 96 return result; 97 } 98 99 /// <summary> 100 /// 根据接口实现标记获取,当前泛型接口实例 101 /// </summary> 102 /// <typeparam name="T">传入的泛型接口</typeparam> 103 /// <param name="implFlag">接口实现类实现标记</param> 104 /// <param name="initArgType">构造函数参数类型数组</param> 105 /// <param name="initArgVal">构造函数参数值数组</param> 106 /// <returns>接口实例</returns> 107 public static T GetInterfaceImpl<T>(string implFlag, Type[] initArgType, object[] initArgVal) 108 { 109 T instance = default(T); 110 Type implType; 111 if (IInstances.TryGetValue(implFlag, out implType)) 112 { 113 try 114 { 115 ConstructorInfo construct = implType.GetConstructor(initArgType); 116 if (construct != null) 117 instance = (T)construct.Invoke(initArgVal); 118 } 119 catch (ArgumentException e) 120 { 121 throw new ArgumentException("反射生成实例失败,清检查构造函数参数信息。"); 122 } 123 } 124 return instance; 125 } 126 127 /// <summary> 128 /// 根据接口实例标记,获取接口实现,无构造函数参数 129 /// </summary> 130 /// <typeparam name="T">泛型接口</typeparam> 131 /// <param name="implFlag">实现标记</param> 132 /// <returns>接口实例</returns> 133 public static T GetInterfaceImpl<T>(string implFlag) 134 { 135 T instance = default(T); 136 Type implType; 137 if (IInstances.TryGetValue(implFlag, out implType)) 138 { 139 try 140 { 141 instance = (T)System.Activator.CreateInstance(implType); 142 } 143 catch (ArgumentException e) 144 { 145 throw new ArgumentException("反射创建实例失败,请核实参数类型是否正确。"); 146 } 147 } 148 return instance; 149 } 150 }
DynamicDllService有三个公开方法,
InitAllInstance:用于在程序启动时,将所有数据中的dll加InitAllInstance中。
SaveFile:是保存dlll并同时动态加载dll中接口实现类和标记,这里注册到该类的静态私有字典(IInstances)中
static T GetInterfaceImpl<T>(string implFlag):最终通过翻新方法返回泛型实例接口,T为所需要到的泛型接口,implFlag:实现类标记。