热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

(三)学习了解OrchardCore笔记——灵魂中间件ModularTenantContainerMiddleware的第一行①的模块部分

了解到了OrchardCore主要由两个中间件(ModularTenantContainerMiddleware和ModularTenantRouterMiddleware)构成,下面开始了解

了解到了OrchardCore主要由两个中间件(ModularTenantContainerMiddleware和ModularTenantRouterMiddleware)构成,下面开始了解ModularTenantContainerMiddleware中间件第一行代码。

了解asp.net core机制的都知道中间件是由构造函数和Invoke(或者InokeAsync)方法构成,构造函数不过就是个依赖注入的过程,Invoke也可以直接依赖注入,两者的区别是构造函数时全局的,Invoke只是当次请求,所以从Inovke开始了解!

public async Task Invoke(HttpContext httpContext)
// Ensure all ShellContext are loaded and available.
await _shellHost.InitializeAsync();
var shellSettings = _runningShellTable.Match(httpContext);
// We only serve the next request if the tenant has been resolved.
if (shellSettings != null)
if (shellSettings.State == TenantState.Initializing)
httpContext.Response.Headers.Add(HeaderNames.RetryAfter, "10");
httpContext.Response.StatusCode = StatusCodes.Status503ServiceUnavailable;
await httpContext.Response.WriteAsync("The requested tenant is currently initializing.");
return;
// Makes 'RequestServices' aware of the current 'ShellScope'.
httpContext.UseShellScopeServices();
var shellScope = await _shellHost.GetScopeAsync(shellSettings);
// Holds the 'ShellContext' for the full request.
httpContext.Features.Set(new ShellContextFeature
ShellCOntext= shellScope.ShellContext,
OriginalPath = httpContext.Request.Path,
OriginalPathBase = httpContext.Request.PathBase
await shellScope.UsingAsync(scope = _next.Invoke(httpContext));
}

现在从第一行代码_shellHost.InitializeAsync()开始看起。_shellHost是一个IShellHost单例,是类ShellHost的实例化,这个很明显就是asp.net core自带的依赖注入生成的(这是在注册服务的时候已经配置好的,具体追踪services.AddOrchardCms就很清晰了)。现在直接找ShellHost的InitializeAsync方法和PreCreateAndRegisterShellsAsync方法。

public async Task InitializeAsync()
if (!_initialized)
// Prevent concurrent requests from creating all shells multiple times
await _initializingSemaphore.WaitAsync();
if (!_initialized)
await PreCreateAndRegisterShellsAsync();
finally
_initialized = true;
_initializingSemaphore.Release();
private async Task PreCreateAndRegisterShellsAsync()
if (_logger.IsEnabled(LogLevel.Information))
_logger.LogInformation("Start creation of shells");
// Load all extensions and features so that the controllers are registered in
// 'ITypeFeatureProvider' and their areas defined in the application conventions.
var features = _extensionManager.LoadFeaturesAsync();
// Is there any tenant right now?
var allSettings = (await _shellSettingsManager.LoadSettingsAsync()).Where(CanCreateShell).ToArray();
var defaultSettings = allSettings.FirstOrDefault(s = s.Name == ShellHelper.DefaultShellName);
var otherSettings = allSettings.Except(new[] { defaultSettings }).ToArray();
await features;
// The 'Default' tenant is not running, run the Setup.
if (defaultSettings?.State != TenantState.Running)
var setupCOntext= await CreateSetupContextAsync(defaultSettings);
AddAndRegisterShell(setupContext);
allSettings = otherSettings;
if (allSettings.Length 0)
// Pre-create and register all tenant shells.
foreach (var settings in allSettings)
AddAndRegisterShell(new ShellContext.PlaceHolder { Settings = settings });
if (_logger.IsEnabled(LogLevel.Information))
_logger.LogInformation("Done pre-creating and registering shells");
}

InitializeAsync从字面上看就是初始的过程ShellHost(这个不知道怎么翻译比较准确)的过程,从代码上看也是如此。

首先通过_initialized判断是否初始化过(开始没赋值肯定false),在初始化结束_initialized就会设置为true,往下几行的finally里面可以看到_initialized = true,因为ShellHost是单例,也就是启动程序之后就会一直维持_initialized = true,简单说就是启动时会初始化一次ShellHost。

再往下看,await _initializingSemaphore.WaitAsync,这个上面也有注释防止并发请求多次创建所有shells,这个开始我也不懂的时候直接理解为lock后面finally的_initializingSemaphore.Release就是lock结束,后面找了点资料试了下,其实就是只有一个请求可以进入初始化,构造函数只允许一个请求(SemaphoreSlim _initializingSemaphore = new SemaphoreSlim(1)),之所以说请求时因为这是中间件里面啊,每个请求都要通过中间件(这个得了解asp.net core中间件的原理),看了OrchardCore真给我带来不少知识点,没遇到这个我只知道多线程用lock锁住,原来还可以有这样的细节。

然后try里又确认一次有没初始化(判断_initialized),为啥呢?我一脸懵逼,后面感觉好像可以接受,比较前面时有很多请求,说不定有个某个请求已经初始化ShellHost了呢,所以_initializingSemaphore.WaitAsync开始后又要确认一次。如果前面不先确认,那么又全部只能一个请求进来,这就很不合理了。我快被绕晕了,真佩服开发者的大脑。

终于来到PreCreateAndRegisterShellsAsync方法了。_loggoer部分直接跳过,就是asp.net core的日志记录而已。OrchardCore是一个模块化多租户的cms,模块化就体现在这里。

var features = _extensionManager.LoadFeaturesAsync();

加载所有的功能,ExtensionManager(OrchardCore\OrchardCore\Extensions\ExtensionManager.cs)的LoadFeaturesAsync方法。

public async Task IEnumerable FeatureEntry LoadFeaturesAsync()
await EnsureInitializedAsync();
return _features.Values;
}

确保初始化并返回FeatureEntry集合,也就是所有功能的集合。现在进入EnsureInitializedAsync方法看看:

private async Task EnsureInitializedAsync()
if (_isInitialized)
return;
await _semaphore.WaitAsync();
if (_isInitialized)
return;
var modules = _applicationContext.Application.Modules;
var loadedExtensiOns= new ConcurrentDictionary string, ExtensionEntry ();
// Load all extensions in parallel
await modules.ForEachAsync((module) =
if (!module.ModuleInfo.Exists)
return Task.CompletedTask;
var manifestInfo = new ManifestInfo(module.ModuleInfo);
var extensiOnInfo= new ExtensionInfo(module.SubPath, manifestInfo, (mi, ei) =
return _featuresProvider.GetFeatures(ei, mi);
var entry = new ExtensionEntry
ExtensiOnInfo= extensionInfo,
Assembly = module.Assembly,
ExportedTypes = module.Assembly.ExportedTypes
loadedExtensions.TryAdd(module.Name, entry);
return Task.CompletedTask;
var loadedFeatures = new Dictionary string, FeatureEntry ();
// Get all valid types from any extension
var allTypesByExtension = loadedExtensions.SelectMany(extension =
extension.Value.ExportedTypes.Where(IsComponentType)
.Select(type = new
ExtensiOnEntry= extension.Value,
Type = type
})).ToArray();
var typesByFeature = allTypesByExtension
.GroupBy(typeByExtension = GetSourceFeatureNameForType(
typeByExtension.Type,
typeByExtension.ExtensionEntry.ExtensionInfo.Id))
.ToDictionary(
group = group.Key,
group = group.Select(typesByExtension = typesByExtension.Type).ToArray());
foreach (var loadedExtension in loadedExtensions)
var extension = loadedExtension.Value;
foreach (var feature in extension.ExtensionInfo.Features)
// Features can have no types
if (typesByFeature.TryGetValue(feature.Id, out var featureTypes))
foreach (var type in featureTypes)
_typeFeatureProvider.TryAdd(type, feature);
else
featureTypes = Array.Empty Type ();
loadedFeatures.Add(feature.Id, new CompiledFeatureEntry(feature, featureTypes));
// Feature infos and entries are ordered by priority and dependencies.
_featureInfos = Order(loadedFeatures.Values.Select(f = f.FeatureInfo));
_features = _featureInfos.ToDictionary(f = f.Id, f = loadedFeatures[f.Id]);
// Extensions are also ordered according to the weight of their first features.
_extensiOnsInfos= _featureInfos.Where(f = f.Id == f.Extension.Features.First().Id)
.Select(f = f.Extension);
_extensiOns= _extensionsInfos.ToDictionary(e = e.Id, e = loadedExtensions[e.Id]);
_isInitialized = true;
finally
_semaphore.Release();
}

前面判断初始化,多请求方面跟之前的是一样的,这里我又不理解了,之前不是判断过了吗,难道是因为这是个Task的原因导致可能跳出到其它线程!这里先跳过!

var modules = _applicationContext.Application.Modules;这里是通过反射得到所有的模块,至于过程真是一言难尽啊,这里卡了我好几个星期才明白究竟是个怎样的过程。

看下OrchardCore\OrchardCore.Abstractions\Modules\ModularApplicationContext.cs这个类

public interface IApplicationContext
Application Application { get; }
public class ModularApplicationContext : IApplicationContext
private readonly IHostEnvironment _environment;
private readonly IEnumerable IModuleNamesProvider _moduleNamesProviders;
private Application _application;
private static readonly object _initLock = new object();
public ModularApplicationContext(IHostEnvironment environment, IEnumerable IModuleNamesProvider moduleNamesProviders)
_envirOnment= environment;
_moduleNamesProviders = moduleNamesProviders;
public Application Application
EnsureInitialized();
return _application;
private void EnsureInitialized()
if (_application == null)
lock (_initLock)
if (_application == null)
_application = new Application(_environment, GetModules());
private IEnumerable Module GetModules()
var modules = new ConcurrentBag Module ();
modules.Add(new Module(_environment.ApplicationName, true));
var names = _moduleNamesProviders
.SelectMany(p = p.GetModuleNames())
.Where(n = n != _environment.ApplicationName)
.Distinct();
Parallel.ForEach(names, new ParallelOptions { MaxDegreeOfParallelism = 8 }, (name) =
modules.Add(new Module(name, false));
return modules;
}

明显就是初始化Application,然后返回Application,首先看EnsureInitialized方法,然后就直接看GetModules方法。

第一行var modules = new ConcurrentBag Module 又触碰到我的知识盲点了,作为一个只会用List T 的人,我真的完全不知道ConcurrentBag T 是什么,马上msdn下,原来ConcurrentBag T 也是个集合,但是它是线程安全的。ok,集合开始添加数据,首先把当前项目的名称OrchardCore.Cms.Web给加进去(modules.Add(new Module(_environment.ApplicationName, true));)。然后看

var names = _moduleNamesProviders
.SelectMany(p = p.GetModuleNames())
.Where(n = n != _environment.ApplicationName)
.Distinct();

找到_moduleNamesProviders的实例(虽然是个集合,但是只有一个类实现了)AssemblyAttributeModuleNamesProvider,打开这个类,直接看构造函数。

public class AssemblyAttributeModuleNamesProvider : IModuleNamesProvider
private readonly List string _moduleNames;
public AssemblyAttributeModuleNamesProvider(IHostEnvironment hostingEnvironment)
var assembly = Assembly.Load(new AssemblyName(hostingEnvironment.ApplicationName));
_moduleNames = assembly.GetCustomAttributes ModuleNameAttribute ().Select(m = m.Name).ToList();
public IEnumerable string GetModuleNames()
return _moduleNames;
}

明显的反射,通过读取当前程序的程序集hostingEnvironment.ApplicationName就是OrchardCore.Cms.Web,也就是获取OrchardCore.Cms.Web.dll所有自定义ModuleName属性的Name集合(看下ModuleNameAttribute可以明白就是构造函数传入的那个字符串)。编译一下程序,用反编译工具读取OrchardCore.Cms.Web.dll(ModuleName哪里来的下篇再说,这也是我一直不明白卡了好几个星期的地方)可以看到如下的ModuleName

返回ModularApplicationContext继续看,就是用linq调用GetModuleNames方法把List string 抽象成集合IEnumerable string 通过筛选唯一值Distinct返回(当然排除掉当前项目,毕竟前面已经加入过线程安全集合了modules)names,然后通过多线程方法加入前面提到那个线程安全集合modules。然后把线程安全集合返回。

var names = _moduleNamesProviders
.SelectMany(p = p.GetModuleNames())
.Where(n = n != _environment.ApplicationName)
.Distinct();
Parallel.ForEach(names, new ParallelOptions { MaxDegreeOfParallelism = 8 }, (name) =
modules.Add(new Module(name, false));
});

学c#第一个多线程类就是Parallel,因此很清楚就是限制最大8个线程(为啥要限制最大8个线程,难道作者是用老i7那种4核8线程那种?),把names分别再多个线程传递给Module实例化然后加入线程安全集合modules返回出去给Application做构造函数的是参数实例化,这也是为啥要用线程安全集合的原因吧(我真哭了,像我这种菜鸟估计只会List T ,然后foreach这种低效率的单线程方法了)。

ok,贴下Application类

public class Application
private readonly Dictionary string, Module _modulesByName;
private readonly List Module _modules;
public const string ModulesPath = "Areas";
public const string ModulesRoot = ModulesPath + "/";
public const string ModuleName = "Application Main Feature";
public const string ModuleDescription = "Provides components defined at the application level.";
public static readonly string ModulePriority = int.MinValue.ToString();
public const string ModuleCategory = "Application";
public const string DefaultFeatureId = "Application.Default";
public const string DefaultFeatureName = "Application Default Feature";
public const string DefaultFeatureDescription = "Adds a default feature to the application's module.";
public Application(IHostEnvironment environment, IEnumerable Module modules)
Name = environment.ApplicationName;
Path = environment.ContentRootPath;
Root = Path + '/';
ModulePath = ModulesRoot + Name;
ModuleRoot = ModulePath + '/';
Assembly = Assembly.Load(new AssemblyName(Name));
_modules = new List Module (modules);
_modulesByName = _modules.ToDictionary(m = m.Name, m = m);
public string Name { get; }
public string Path { get; }
public string Root { get; }
public string ModulePath { get; }
public string ModuleRoot { get; }
public Assembly Assembly { get; }
public IEnumerable Module Modules = _modules;
public Module GetModule(string name)
if (!_modulesByName.TryGetValue(name, out var module))
return new Module(string.Empty);
return module;
}

前面获取所有模块也就是Modules属性可以清晰的从构造函数那里看到就是我们刚刚返回的线程安全集合modules,绕来绕去我都快被绕晕了,真想一个指针指过去。好了,继续回到我们的初始化功能,就是ExtensionManager的EnsureInitializedAsync方法

private async Task EnsureInitializedAsync()
if (_isInitialized)
return;
await _semaphore.WaitAsync();
if (_isInitialized)
return;
var modules = _applicationContext.Application.Modules;
var loadedExtensiOns= new ConcurrentDictionary string, ExtensionEntry ();
// Load all extensions in parallel
await modules.ForEachAsync((module) =
if (!module.ModuleInfo.Exists)
return Task.CompletedTask;
var manifestInfo = new ManifestInfo(module.ModuleInfo);
var extensiOnInfo= new ExtensionInfo(module.SubPath, manifestInfo, (mi, ei) =
return _featuresProvider.GetFeatures(ei, mi);
var entry = new ExtensionEntry
ExtensiOnInfo= extensionInfo,
Assembly = module.Assembly,
ExportedTypes = module.Assembly.ExportedTypes
loadedExtensions.TryAdd(module.Name, entry);
return Task.CompletedTask;
var loadedFeatures = new Dictionary string, FeatureEntry ();
// Get all valid types from any extension
var allTypesByExtension = loadedExtensions.SelectMany(extension =
extension.Value.ExportedTypes.Where(IsComponentType)
.Select(type = new
ExtensiOnEntry= extension.Value,
Type = type
})).ToArray();
var typesByFeature = allTypesByExtension
.GroupBy(typeByExtension = GetSourceFeatureNameForType(
typeByExtension.Type,
typeByExtension.ExtensionEntry.ExtensionInfo.Id))
.ToDictionary(
group = group.Key,
group = group.Select(typesByExtension = typesByExtension.Type).ToArray());
foreach (var loadedExtension in loadedExtensions)
var extension = loadedExtension.Value;
foreach (var feature in extension.ExtensionInfo.Features)
// Features can have no types
if (typesByFeature.TryGetValue(feature.Id, out var featureTypes))
foreach (var type in featureTypes)
_typeFeatureProvider.TryAdd(type, feature);
else
featureTypes = Array.Empty Type ();
loadedFeatures.Add(feature.Id, new CompiledFeatureEntry(feature, featureTypes));
// Feature infos and entries are ordered by priority and dependencies.
_featureInfos = Order(loadedFeatures.Values.Select(f = f.FeatureInfo));
_features = _featureInfos.ToDictionary(f = f.Id, f = loadedFeatures[f.Id]);
// Extensions are also ordered according to the weight of their first features.
_extensiOnsInfos= _featureInfos.Where(f = f.Id == f.Extension.Features.First().Id)
.Select(f = f.Extension);
_extensiOns= _extensionsInfos.ToDictionary(e = e.Id, e = loadedExtensions[e.Id]);
_isInitialized = true;
finally
_semaphore.Release();
}

现在modules有了,下行是var loadedExtensiOns= new ConcurrentDictionary string, ExtensionEntry 经过刚刚的线程安全集合,ConcurrentDictionary虽然接触不多也明白这个是线程安全字典(学依赖注入的时候遇到过)。既然是线程安全字典,下面肯定又是多线程来填充这个线程安全字典loadedExtensions,这里用了一个ForEachAsync的扩展方法分配多个任务,把每个modules的ManifestInfo分析出来的功能加入ConcurrentDictionary。具体细说估计篇幅太长了,先简单介绍下吧。之前modules就是OrchardCore.Modules、OrchardCore.Modules.Cms和OrchardCore.Themes这三个解决方案下的项目集合,我们称作模块集合,每个模块由一个或者多个功能组成,现在就是把功能封装成线程安全的字典集合,而功能是有重复的,可能多个模块用同一个功能,所以线程安全很重要!下篇要解释下反射那些ModuleName是如何实现的(毕竟困扰我这么久的东西必须记录下才能印象深刻),我看下下篇能不能把功能拆出来说吧。

文章来源于https://www.cnblogs.com/ShuiSen/p/13282938.html


推荐阅读
  • Spring学习(4):Spring管理对象之间的关联关系
    本文是关于Spring学习的第四篇文章,讲述了Spring框架中管理对象之间的关联关系。文章介绍了MessageService类和MessagePrinter类的实现,并解释了它们之间的关联关系。通过学习本文,读者可以了解Spring框架中对象之间的关联关系的概念和实现方式。 ... [详细]
  • 使用圣杯布局模式实现网站首页的内容布局
    本文介绍了使用圣杯布局模式实现网站首页的内容布局的方法,包括HTML部分代码和实例。同时还提供了公司新闻、最新产品、关于我们、联系我们等页面的布局示例。商品展示区包括了车里子和农家生态土鸡蛋等产品的价格信息。 ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • 在重复造轮子的情况下用ProxyServlet反向代理来减少工作量
    像不少公司内部不同团队都会自己研发自己工具产品,当各个产品逐渐成熟,到达了一定的发展瓶颈,同时每个产品都有着自己的入口,用户 ... [详细]
  • springmvc学习笔记(十):控制器业务方法中通过注解实现封装Javabean接收表单提交的数据
    本文介绍了在springmvc学习笔记系列的第十篇中,控制器的业务方法中如何通过注解实现封装Javabean来接收表单提交的数据。同时还讨论了当有多个注册表单且字段完全相同时,如何将其交给同一个控制器处理。 ... [详细]
  • Python瓦片图下载、合并、绘图、标记的代码示例
    本文提供了Python瓦片图下载、合并、绘图、标记的代码示例,包括下载代码、多线程下载、图像处理等功能。通过参考geoserver,使用PIL、cv2、numpy、gdal、osr等库实现了瓦片图的下载、合并、绘图和标记功能。代码示例详细介绍了各个功能的实现方法,供读者参考使用。 ... [详细]
  • FeatureRequestIsyourfeaturerequestrelatedtoaproblem?Please ... [详细]
  • Android开发实现的计时器功能示例
    本文分享了Android开发实现的计时器功能示例,包括效果图、布局和按钮的使用。通过使用Chronometer控件,可以实现计时器功能。该示例适用于Android平台,供开发者参考。 ... [详细]
  • WebSocket与Socket.io的理解
    WebSocketprotocol是HTML5一种新的协议。它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送 ... [详细]
  • 本文介绍了如何使用C#制作Java+Mysql+Tomcat环境安装程序,实现一键式安装。通过将JDK、Mysql、Tomcat三者制作成一个安装包,解决了客户在安装软件时的复杂配置和繁琐问题,便于管理软件版本和系统集成。具体步骤包括配置JDK环境变量和安装Mysql服务,其中使用了MySQL Server 5.5社区版和my.ini文件。安装方法为通过命令行将目录转到mysql的bin目录下,执行mysqld --install MySQL5命令。 ... [详细]
  • 本文介绍了在处理不规则数据时如何使用Python自动提取文本中的时间日期,包括使用dateutil.parser模块统一日期字符串格式和使用datefinder模块提取日期。同时,还介绍了一段使用正则表达式的代码,可以支持中文日期和一些特殊的时间识别,例如'2012年12月12日'、'3小时前'、'在2012/12/13哈哈'等。 ... [详细]
  • 本文详细介绍了使用C#实现Word模版打印的方案。包括添加COM引用、新建Word操作类、开启Word进程、加载模版文件等步骤。通过该方案可以实现C#对Word文档的打印功能。 ... [详细]
  • 带添加按钮的GridView,item的删除事件
    先上图片效果;gridView无数据时显示添加按钮,有数据时,第一格显示添加按钮,后面显示数据:布局文件:addr_manage.xml<?xmlve ... [详细]
  • 本文介绍了NetCore WebAPI开发的探索过程,包括新建项目、运行接口获取数据、跨平台部署等。同时还提供了客户端访问代码示例,包括Post函数、服务器post地址、api参数等。详细讲解了部署模式选择、框架依赖和独立部署的区别,以及在Windows和Linux平台上的部署方法。 ... [详细]
author-avatar
海底来的沙3
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有