NopCommerce源码架构详解--TypeFinder程序集类型自动查找及操作相关源码分析

今天我们来看看NopCommerce源码一个值得借鉴的设计,TypeFinder类型自动查找。它会自动去程序的bin目录或者AppDomain下查找满足指定类型的的所有类型的集合。比如:

 
  1. var consumers = typeFinder.FindClassesOfType(typeof(IConsumer<>)).ToList();

上面代码就是查找实现接口所有的类。仅可以查找指定类型,还可以获取全部的程序集,如下:

 
  1. typeFinder.GetAssemblies().ToArray()

利用以上两点就可以利用IoC容器对查找的类型进行遍历注册。下面是我从nopCommerce源码中找到关于IoC和TypeFinder使用的代码。

 
  1. builder.RegisterControllers(typeFinder.GetAssemblies().ToArray());
 
  1. var consumers = typeFinder.FindClassesOfType(typeof(IConsumer<>)).ToList();
  2. foreach (var consumer in consumers)
  3. {
  4. builder.RegisterType(consumer)
  5. .As(consumer.FindInterfaces((type, criteria) =>
  6. {
  7. var isMatch = type.IsGenericType && ((Type)criteria).IsAssignableFrom(type.GetGenericTypeDefinition());
  8. return isMatch;
  9. }, typeof(IConsumer<>)))
  10. .InstancePerLifetimeScope();
  11. }

 

从上面我们知道了Nop中TypeFinder的作用,那么它又是怎么实现的呢?下面我就来说说TypeFinder的原理。同样我们来看看这部分相关代码的类图:

TypeFinder相关的类及作用:

1、ITypeFinder:类型查找抽象接口

2、AppDomainTypeFinder:在当前运行中的AppDomain中查找相关类型的集合

3、WebAppTypeFinder:在当前的web App的bin查找相关类型的集合

4、DependencyRegistrar:依赖注册

5、NopEngine:Nop引擎

接下来我们来看看这些类的核心代码及作用。

Nop.Core.Infrastructure.ITypeFinder

 
  1. public interface ITypeFinder
  2. {
  3. IList<Assembly> GetAssemblies();
  4.  
  5. IEnumerable<Type> FindClassesOfType(Type assignTypeFrom, bool onlyConcreteClasses = true);
  6.  
  7. IEnumerable<Type> FindClassesOfType(Type assignTypeFrom, IEnumerable<Assembly> assemblies, bool onlyConcreteClasses = true);
  8.  
  9. IEnumerable<Type> FindClassesOfType<T>(bool onlyConcreteClasses = true);
  10.  
  11. IEnumerable<Type> FindClassesOfType<T>(IEnumerable<Assembly> assemblies, bool onlyConcreteClasses = true);
  12. }

 

Nop.Core.Infrastructure.AppDomainTypeFinder

 
  1. public class AppDomainTypeFinder : ITypeFinder
  2. {
  3. #region Fields
  4.  
  5. private bool ignoreReflectionErrors = true;
  6. private bool loadAppDomainAssemblies = true;
  7. //忽略一些系统程序集及其它特殊程序集
  8. private string assemblySkipLoadingPattern = "^System|^mscorlib|^Microsoft|^AjaxControlToolkit|^Antlr3|^Autofac|^AutoMapper|^Castle|^ComponentArt|^CppCodeProvider|^DotNetOpenAuth|^EntityFramework|^EPPlus|^FluentValidation|^ImageResizer|^itextsharp|^log4net|^MaxMind|^MbUnit|^MiniProfiler|^Mono.Math|^mvcContrib|^Newtonsoft|^NHibernate|^nunit|^Org.Mentalis|^PerlRegex|^QuickGraph|^Recaptcha|^Remotion|^RestSharp|^Rhino|^Telerik|^Iesi|^TestDriven|^TestFu|^UserAgentStringLibrary|^VJSharpCodeProvider|^WebActivator|^WebDev|^WebGrease";
  9. private string assemblyRestrictToLoadingPattern = ".*";
  10. private IList<string> assemblyNames = new List<string>();
  11.  
  12. #endregion
  13.  
  14.  
  15. #region Methods
  16.  
  17. public IEnumerable<Type> FindClassesOfType<T>(bool onlyConcreteClasses = true)
  18. {
  19. return FindClassesOfType(typeof(T), onlyConcreteClasses);
  20. }
  21.  
  22. public IEnumerable<Type> FindClassesOfType(Type assignTypeFrom, bool onlyConcreteClasses = true)
  23. {
  24. return FindClassesOfType(assignTypeFrom, GetAssemblies(), onlyConcreteClasses);
  25. }
  26.  
  27. public IEnumerable<Type> FindClassesOfType<T>(IEnumerable<Assembly> assemblies, bool onlyConcreteClasses = true)
  28. {
  29. return FindClassesOfType(typeof (T), assemblies, onlyConcreteClasses);
  30. }
  31.  
  32. public IEnumerable<Type> FindClassesOfType(Type assignTypeFrom,
  33. IEnumerable<Assembly> assemblies, bool onlyConcreteClasses = true)
  34. {
  35. var result = new List<Type>();
  36. try
  37. {
  38. foreach (var a in assemblies)
  39. {
  40. Type[] types = null;
  41. try
  42. {
  43. types = a.GetTypes();
  44. }
  45. catch
  46. {
  47. //Entity Framework 6 doesn't allow getting types (throws an exception)
  48. if (!ignoreReflectionErrors)
  49. {
  50. throw;
  51. }
  52. }
  53. if (types != null)
  54. {
  55. foreach (var t in types)
  56. {
  57. if (assignTypeFrom.IsAssignableFrom(t)
  58. || (assignTypeFrom.IsGenericTypeDefinition && DoesTypeImplementOpenGeneric(t, assignTypeFrom)))
  59. {
  60. if (!t.IsInterface)
  61. {
  62. if (onlyConcreteClasses)
  63. {
  64. if (t.IsClass && !t.IsAbstract)
  65. {
  66. result.Add(t);
  67. }
  68. }
  69. else
  70. {
  71. result.Add(t);
  72. }
  73. }
  74. }
  75. }
  76. }
  77. }
  78. }
  79. catch (ReflectionTypeLoadException ex)
  80. {
  81. var msg = string.Empty;
  82. foreach (var e in ex.LoaderExceptions)
  83. msg += e.Message + Environment.NewLine;
  84.  
  85. var fail = new Exception(msg, ex);
  86. Debug.WriteLine(fail.Message, fail);
  87.  
  88. throw fail;
  89. }
  90. return result;
  91. }
  92.  
  93. /// <summary>获取指定类型的所有实现类</summary>
  94. /// <returns>A list of assemblies that should be loaded by the Nop factory.</returns>
  95. public virtual IList<Assembly> GetAssemblies()
  96. {
  97. var addedAssemblyNames = new List<string>();
  98. var assemblies = new List<Assembly>();
  99.  
  100. if (LoadAppDomainAssemblies)
  101. AddAssembliesInAppDomain(addedAssemblyNames, assemblies);
  102. AddConfiguredAssemblies(addedAssemblyNames, assemblies);
  103.  
  104. return assemblies;
  105. }
  106.  
  107. #endregion
  108.  
  109. #region Utilities
  110.  
  111. /// <summary>
  112. /// 遍历AppDomain下的所有程序集并与指定类型进行比较,如果匹配就加入要返回的List中
  113. /// </summary>
  114. /// <param name="addedAssemblyNames"></param>
  115. /// <param name="assemblies"></param>
  116. private void AddAssembliesInAppDomain(List<string> addedAssemblyNames, List<Assembly> assemblies)
  117. {
  118. foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
  119. {
  120. if (Matches(assembly.FullName))
  121. {
  122. if (!addedAssemblyNames.Contains(assembly.FullName))
  123. {
  124. assemblies.Add(assembly);
  125. addedAssemblyNames.Add(assembly.FullName);
  126. }
  127. }
  128. }
  129. }
  130.  
  131. /// <summary>
  132. /// 添加特定程序集到到集合中
  133. /// </summary>
  134. /// <param name="addedAssemblyNames"></param>
  135. /// <param name="assemblies"></param>
  136. protected virtual void AddConfiguredAssemblies(List<string> addedAssemblyNames, List<Assembly> assemblies)
  137. {
  138. foreach (string assemblyName in AssemblyNames)
  139. {
  140. Assembly assembly = Assembly.Load(assemblyName);
  141. if (!addedAssemblyNames.Contains(assembly.FullName))
  142. {
  143. assemblies.Add(assembly);
  144. addedAssemblyNames.Add(assembly.FullName);
  145. }
  146. }
  147. }
  148.  
  149. /// <summary>
  150. /// Check if a dll is one of the shipped dlls that we know don't need to be investigated.
  151. /// </summary>
  152. /// <param name="assemblyFullName">
  153. /// The name of the assembly to check.
  154. /// </param>
  155. /// <returns>
  156. /// True if the assembly should be loaded into Nop.
  157. /// </returns>
  158. public virtual bool Matches(string assemblyFullName)
  159. {
  160. return !Matches(assemblyFullName, AssemblySkipLoadingPattern)
  161. && Matches(assemblyFullName, AssemblyRestrictToLoadingPattern);
  162. }
  163.  
  164. /// <summary>
  165. /// Check if a dll is one of the shipped dlls that we know don't need to be investigated.
  166. /// </summary>
  167. /// <param name="assemblyFullName">
  168. /// The assembly name to match.
  169. /// </param>
  170. /// <param name="pattern">
  171. /// The regular expression pattern to match against the assembly name.
  172. /// </param>
  173. /// <returns>
  174. /// True if the pattern matches the assembly name.
  175. /// </returns>
  176. protected virtual bool Matches(string assemblyFullName, string pattern)
  177. {
  178. return Regex.IsMatch(assemblyFullName, pattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);
  179. }
  180.  
  181. /// <summary>
  182. /// Makes sure matching assemblies in the supplied folder are loaded in the app domain.
  183. /// </summary>
  184. /// <param name="directoryPath">
  185. /// The physical path to a directory containing dlls to load in the app domain.
  186. /// </param>
  187. protected virtual void LoadMatchingAssemblies(string directoryPath)
  188. {
  189. var loadedAssemblyNames = new List<string>();
  190. foreach (Assembly a in GetAssemblies())
  191. {
  192. loadedAssemblyNames.Add(a.FullName);
  193. }
  194.  
  195. if (!Directory.Exists(directoryPath))
  196. {
  197. return;
  198. }
  199. //获取指定目录下以.dll为结尾的文件
  200. foreach (string dllPath in Directory.GetFiles(directoryPath, "*.dll"))
  201. {
  202. try
  203. {
  204. var an = AssemblyName.GetAssemblyName(dllPath);
  205. if (Matches(an.FullName) && !loadedAssemblyNames.Contains(an.FullName))
  206. {
  207. App.Load(an);//加载程序集到当前AppDomain中
  208. }
  209.  
  210. //old loading stuff
  211. //Assembly a = Assembly.ReflectionOnlyLoadFrom(dllPath);
  212. //if (Matches(a.FullName) && !loadedAssemblyNames.Contains(a.FullName))
  213. //{
  214. // App.Load(a.FullName);
  215. //}
  216. }
  217. catch (BadImageFormatException ex)
  218. {
  219. Trace.TraceError(ex.ToString());
  220. }
  221. }
  222. }
  223.  
  224. /// <summary>
  225. /// Does type implement generic?
  226. /// </summary>
  227. /// <param name="type"></param>
  228. /// <param name="openGeneric"></param>
  229. /// <returns></returns>
  230. protected virtual bool DoesTypeImplementOpenGeneric(Type type, Type openGeneric)
  231. {
  232. try
  233. {
  234. var genericTypeDefinition = openGeneric.GetGenericTypeDefinition();
  235. foreach (var implementedInterface in type.FindInterfaces((objType, objCriteria) => true, null))
  236. {
  237. if (!implementedInterface.IsGenericType)
  238. continue;
  239.  
  240. var isMatch = genericTypeDefinition.IsAssignableFrom(implementedInterface.GetGenericTypeDefinition());
  241. return isMatch;
  242. }
  243. return false;
  244. }catch
  245. {
  246. return false;
  247. }
  248. }
  249.  
  250. #endregion
  251. }

 

Nop.Core.Infrastructure.WebAppTypeFinder

WebAppTypeFinder是继承于类AppDomainTypeFinder

 
  1. public class WebAppTypeFinder : AppDomainTypeFinder
  2. {
  3. #region Fields
  4.  
  5. private bool _ensureBinFolderAssembliesLoaded = true;
  6. private bool _binFolderAssembliesLoaded = false;
  7.  
  8. #endregion
  9.  
  10. #region Ctor
  11.  
  12. public WebAppTypeFinder(NopConfig config)
  13. {
  14. this._ensureBinFolderAssembliesLoaded = config.DynamicDiscovery;
  15. }
  16.  
  17. #endregion
  18.  
  19. #region Properties
  20.  
  21. /// <summary>
  22. /// Gets or sets wether assemblies in the bin folder of the web application should be specificly checked for beeing loaded on application load. This is need in situations where plugins need to be loaded in the AppDomain after the application been reloaded.
  23. /// </summary>
  24. public bool EnsureBinFolderAssembliesLoaded
  25. {
  26. get { return _ensureBinFolderAssembliesLoaded; }
  27. set { _ensureBinFolderAssembliesLoaded = value; }
  28. }
  29.  
  30. #endregion
  31.  
  32. #region Methods
  33.  
  34. /// <summary>
  35. /// Gets a physical disk path of \Bin directory
  36. /// </summary>
  37. /// <returns>The physical path. E.g. "c:\inetpub\wwwroot\bin"</returns>
  38. public virtual string GetBinDirectory()
  39. {
  40. if (HostingEnvironment.IsHosted)
  41. {
  42. //hosted
  43. return HttpRuntime.BinDirectory;
  44. }
  45. else
  46. {
  47. //not hosted. For example, run either in unit tests
  48. return AppDomain.CurrentDomain.BaseDirectory;
  49. }
  50. }
  51.  
  52. public override IList<Assembly> GetAssemblies()
  53. {
  54. if (this.EnsureBinFolderAssembliesLoaded && !_binFolderAssembliesLoaded)
  55. {
  56. _binFolderAssembliesLoaded = true;
  57. string binPath = GetBinDirectory();
  58. //binPath = _webHelper.MapPath("~/bin");
  59. LoadMatchingAssemblies(binPath);
  60. }
  61.  
  62. return base.GetAssemblies();
  63. }
  64.  
  65. #endregion
  66. }

 

在Nop.Core.Infrastructure.NopEngine类中有以下方法:

 
  1. protected virtual void RegisterDependencies(NopConfig config)
  2. {
  3. var builder = new ContainerBuilder();
  4. var container = builder.Build();
  5.  
  6. //we create new instance of ContainerBuilder
  7. //because Build() or Update() method can only be called once on a ContainerBuilder.
  8.  
  9.  
  10. //dependencies
  11. var typeFinder = new WebAppTypeFinder(config);
  12. builder = new ContainerBuilder();
  13. builder.RegisterInstance(config).As<NopConfig>().SingleInstance();
  14. builder.RegisterInstance(this).As<IEngine>().SingleInstance();
  15. //ITypeFinder接口用WebAppTypeFinder的单例对象注册绑定
  16.   builder.RegisterInstance(typeFinder).As<ITypeFinder>().SingleInstance();
  17. builder.Update(container);
  18.  
  19. //register dependencies provided by other assemblies
  20. builder = new ContainerBuilder();
  21. var drTypes = typeFinder.FindClassesOfType<IDependencyRegistrar>();
  22. var drInstances = new List<IDependencyRegistrar>();
  23. foreach (var drType in drTypes)
  24. drInstances.Add((IDependencyRegistrar) Activator.CreateInstance(drType));
  25. //sort
  26. drInstances = drInstances.AsQueryable().OrderBy(t => t.Order).ToList();
  27. foreach (var dependencyRegistrar in drInstances)
  28. dependencyRegistrar.Register(builder, typeFinder);
  29. builder.Update(container);
  30.  
  31.  
  32. this._containerManager = new ContainerManager(container);
  33.  
  34. //set dependency resolver
  35. DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
  36. }

 

可以看到上面 typeFinder.FindClassesOfType<IDependencyRegistrar>(),查找依赖注入抽象接口的IDependencyRegistrar的全部实现类型的集合,并依次调用其方法Register。

Nop依赖注入相关原理分析看看我以前的文章NopCommerce源码架构详解--Autofac依赖注入分析

同样我们在Nop.Web.Framework.DependencyRegistrar看到下面使用ITypeFinder注册依赖对象,如下:

 
  1. public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder)
  2. {
  3. //...省略其它代码
  4.  
  5. //controllers
  6. builder.RegisterControllers(typeFinder.GetAssemblies().ToArray());
  7.  
  8. //...省略其它代码
  9.  
  10. //Register event consumers
  11. var consumers = typeFinder.FindClassesOfType(typeof(IConsumer<>)).ToList();
  12. foreach (var consumer in consumers)
  13. {
  14. builder.RegisterType(consumer)
  15. .As(consumer.FindInterfaces((type, criteria) =>
  16. {
  17. var isMatch = type.IsGenericType && ((Type)criteria).IsAssignableFrom(type.GetGenericTypeDefinition());
  18. return isMatch;
  19. }, typeof(IConsumer<>)))
  20. .InstancePerLifetimeScope();
  21. }
  22. }

蓝狐软件工作室 » NopCommerce源码架构详解--TypeFinder程序集类型自动查找及操作相关源码分析

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本次版本没有大的新功能,因为我们主要重点放在ASP.NET 4.0迁移,更多的功能维护和修改漏洞,但我们有做出以下修改亮点:移到ASP.NET4.0(需要装VS2010用于源代码编辑)简化数据访问。目前使用ORM(Entity framework 4.0)成QuickBook性能优化以下方面有提升:USA EPAY(成)支付模块(感谢Chris Curtis)QuickPay支付方式中添加了退款和收款GOOGLE广告成更新PAYPAL SOAP API至最新版(63.0)全局上下文事件(开发人员)把Assigned to product variants打折拆分为Assigned to product variants和Assigned to categories新的促销管理界面PriceGrabber / Yahoo Shopping, become.com的促销信息供应源新的themoneyconverter.com汇率信息源(默认显示)根据订单状态让礼品卡激活/禁用(和积分类似)如果订单总额为0,可跳过/隐藏支付方式(可以各个支付方式中配置)产品的规格、属性界面更加友好消息模板和信息本地化界面更加友好产品主图片可以放大店主可以在全店范围内禁用PDF/EXCEL功能(在中等信任级别中可用)Payment/Shipping restrictions by country加上全选功能RSS按钮添加了tooltip资源少量admin界面修改批量选项:删除队列中的EMAIL在投票模块中加入“在首页显示”和“开始/截止日期”的属性选项:是否显示导航栏(俗称面包屑)把MenuControl.ascx移到\Administration\Modules目录订单详细页有更多的用户界面修改包裹单现在保存到file/ImportExport目录中漏洞:产品拷贝功能没有拷贝prices by customer roles在某些服务器上HTML编辑器会有JS脚本问题修正上传image/icon收藏夹图标的问题(不是image/x-icon)一些函数/属性中的拼写错误(Weigth > Weight)修正在IE6中,高级搜索页面的界面问题在点击“继续购物”按钮以后回到错误的页面Administration\Tax\General\TaxRates.ascx文件中,HeaderText字段应该和文本字段匹配修正了语言图标的小问题修正澳大利亚邮政(最小尺寸检查和多包裹)修正UPS的问题(在美国以外发货)修正在一级价格和产品属性中的价格调整修正Assigned to product variant中的打折问题(当打折比产品价格还多时)修正了Assigned to shipping中的打折问题修正了产品批量编辑的问题产品批量编辑页面中文本框SEOHelper.RenderMetaTag()没有创建新标签在用图片(国旗)作为语言选择时,把语言名作为tool tip(html标题)和注释文本显示在image按钮上。我们不需要在更新购物车以后重设客户的默认付款/收货地址(CustomerManager.ResetCheckoutData方法)firefox下载含有空格的文件名会被截断
本次版本没有大的新功能,因为我们主要重点放在ASP.NET 4.0迁移,更多的功能维护和修改漏洞,但我们有做出以下修改 亮点: 移到ASP.NET4.0(需要装VS2010用于源代码编辑) 简化数据访问。目前使用ORM(Entity framework 4.0) 成QuickBook 性能优化 以下方面有提升: USA EPAY(成)支付模块(感谢Chris Curtis) QuickPay支付方式中添加了退款和收款 GOOGLE广告成 更新PAYPAL SOAP API至最新版(63.0) 全局上下文事件(开发人员) 把Assigned to product variants打折拆分为Assigned to product variants和Assigned to categories 新的促销管理界面 PriceGrabber / Yahoo Shopping, become.com的促销信息供应源 新的themoneyconverter.com汇率信息源(默认显示) 根据订单状态让礼品卡激活/禁用(和积分类似) 如果订单总额为0,可跳过/隐藏支付方式(可以各个支付方式中配置) 产品的规格、属性界面更加友好 消息模板和信息本地化界面更加友好 产品主图片可以放大 店主可以在全店范围内禁用PDF/EXCEL功能(在中等信任级别中可用) Payment/Shipping restrictions by country加上全选功能 RSS按钮添加了tooltip资源 少量admin界面修改 批量选项:删除队列中的EMAIL 在投票模块中加入“在首页显示”和“开始/截止日期”的属性 选项:是否显示导航栏(俗称面包屑) 把MenuControl.ascx移到\Administration\Modules目录 订单详细页有更多的用户界面修改 包裹单现在保存到file/ImportExport目录中 漏洞: 产品拷贝功能没有拷贝prices by customer roles 在某些服务器上HTML编辑器会有JS脚本问题 修正上传image/icon收藏夹图标的问题(不是image/x-icon) 一些函数/属性中的拼写错误(Weigth > Weight) 修正在IE6中,高级搜索页面的界面问题 在点击“继续购物”按钮以后回到错误的页面 Administration\Tax\General\TaxRates.ascx文件中,HeaderText字段应该和文本字段匹配 修正了语言图标的小问题 修正澳大利亚邮政(最小尺寸检查和多包裹) 修正UPS的问题(在美国以外发货) 修正在一级价格和产品属性中的价格调整 修正Assigned to product variant中的打折问题(当打折比产品价格还多时) 修正了Assigned to shipping中的打折问题 修正了产品批量编辑的问题 产品批量编辑页面中文本框 SEOHelper.RenderMetaTag()没有创建新标签 在用图片(国旗)作为语言选择时,把语言名作为tool tip(html标题)和注释文本显示在image按钮上。 我们不需要在更新购物车以后重设客户的默认付款/收货地址(CustomerManager.ResetCheckoutData方法) firefox下载含有空格的文件名会被截断
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值