(五)學習瞭解OrchardCore筆記——靈魂中介軟體ModularTenantContainerMiddleware的第一行②模組的功能部分
在(三)的時候已經說到模組集合用ForEachAsync的擴充套件方法分配多個任務,把每個modules的ManifestInfo分析出來的功能加入ConcurrentDictionary。我們先看看這個擴充套件方法:
public static class EnumerableExtensions { public static Task ForEachAsync<T>(this IEnumerable<T> source, Func<T, Task> body) { var partitionCount = System.Environment.ProcessorCount; return Task.WhenAll( from partition in Partitioner.Create(source).GetPartitions(partitionCount) select Task.Run(async delegate { using (partition) { while (partition.MoveNext()) { await body(partition.Current); } } })); } }
這個查下msdn就非常簡單明瞭,partitionCount獲取可用邏輯處理器的數量(網上也有說是一組邏輯處理器的數量,一組的區別是有的伺服器cpu是多組核心構成的,clr只能在一組邏輯處理器執行?也有人說現在已經可以執行在該伺服器所有邏輯處理器上了,這點我不清楚,因為我沒有這麼多核心的處理器可以測試,這裡當作所有邏輯處理器吧)。然後Partitioner.Create(source)就是把模組集合建立成可排序的程式分割槽,GetPartitions(partitionCount)就是按邏輯處理器的數量分割槽,然後開始按分割槽執行任務,看看partition.MoveNext()就是每個分割槽都是一個接著一個執行到最後一個集合的元素(Task.WhenAll和Task.Run這個不用說估計大家都懂,不懂自己msdn),partition.Current就是當前位置的元素。body就是委託,因此可以清晰的知道ForEachAsync就是把modules集合按邏輯處理器分割槽然後迭代執行委託,天啊,我只會foreach,這種多執行緒操作我也是第一次見。
返回ExtensionManager類繼續看看ForEachAsync這部分程式碼
// 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; });
每個modules集合的元素都執行一遍ForEachAsync裡面的委託(我還是喜歡c裡面指標函式的叫法,因為明顯就是個函式嗎)。看看這個函式不對是委託,傳入引數就是具體的某一個module,先判斷module.ModuleInfo是否存在,之前篇幅沒說這個module.ModuleInfo其實就是每個模組專案(包括主題專案)裡面的Manifest.cs檔案對映到類,看看Module的建構函式就很清楚了(在(三)裡面Application例項化中第二個引數裡面GetModules()新增Module的時候)。也就是說以後自定義模組必須包含這個檔案,不然不會載入進來。接下來就例項化該模組的擴充套件資訊類extensionInfo,這個類很簡單,我們看看ExtensionInfo的程式碼:
public class ExtensionInfo : IExtensionInfo { public ExtensionInfo( string subPath, IManifestInfo manifestInfo, Func<IManifestInfo, IExtensionInfo, IEnumerable<IFeatureInfo>> features) { SubPath = subPath; Manifest = manifestInfo; Features = features(manifestInfo, this); } public string Id => Manifest.ModuleInfo.Id; public string SubPath { get; } public IManifestInfo Manifest { get; } public IEnumerable<IFeatureInfo> Features { get; } public bool Exists => Manifest.Exists; }
裡面主要幾個屬性((三)裡沒追蹤Modules自己追蹤下很清楚這些屬性的值是什麼)。建構函式傳入的三個引數也很明瞭就是例項化給屬性賦值。1、SubPath模組的路徑,自己點下很清晰就是Areas/模組名,這個很屬性吧,沒錯,跟asp.net core的區域一樣。2、Manifest模組的Manifest.cs檔案反射出的類。3、呼叫這個委託給功能集合賦值。我們看看這個委託實現的程式碼(跟ExtensionManager同個目錄下有個Features目錄的FeaturesProvider類):
public class FeaturesProvider : IFeaturesProvider { public const string FeatureProviderCacheKey = "FeatureProvider:Features"; private readonly IEnumerable<IFeatureBuilderEvents> _featureBuilderEvents; public FeaturesProvider(IEnumerable<IFeatureBuilderEvents> featureBuilderEvents) { _featureBuilderEvents = featureBuilderEvents; } public IEnumerable<IFeatureInfo> GetFeatures( IExtensionInfo extensionInfo, IManifestInfo manifestInfo) { var featuresInfos = new List<IFeatureInfo>(); // Features and Dependencies live within this section var features = manifestInfo.ModuleInfo.Features.ToList(); if (features.Count > 0) { foreach (var feature in features) { if (String.IsNullOrWhiteSpace(feature.Id)) { throw new ArgumentException( $"A feature is missing a mandatory 'Id' property in the Module '{extensionInfo.Id}'"); } var featureId = feature.Id; var featureName = feature.Name ?? feature.Id; var featureDependencyIds = feature.Dependencies .Select(e => e.Trim()).ToArray(); if (!int.TryParse(feature.Priority ?? manifestInfo.ModuleInfo.Priority, out int featurePriority)) { featurePriority = 0; } var featureCategory = feature.Category ?? manifestInfo.ModuleInfo.Category; var featureDescription = feature.Description ?? manifestInfo.ModuleInfo.Description; var featureDefaultTenantOnly = feature.DefaultTenantOnly; var featureIsAlwaysEnabled = feature.IsAlwaysEnabled; var context = new FeatureBuildingContext { FeatureId = featureId, FeatureName = featureName, Category = featureCategory, Description = featureDescription, ExtensionInfo = extensionInfo, ManifestInfo = manifestInfo, Priority = featurePriority, FeatureDependencyIds = featureDependencyIds, DefaultTenantOnly = featureDefaultTenantOnly, IsAlwaysEnabled = featureIsAlwaysEnabled }; foreach (var builder in _featureBuilderEvents) { builder.Building(context); } var featureInfo = new FeatureInfo( featureId, featureName, featurePriority, featureCategory, featureDescription, extensionInfo, featureDependencyIds, featureDefaultTenantOnly, featureIsAlwaysEnabled); foreach (var builder in _featureBuilderEvents) { builder.Built(featureInfo); } featuresInfos.Add(featureInfo); } } else { // The Extension has only one feature, itself, and that can have dependencies var featureId = extensionInfo.Id; var featureName = manifestInfo.Name; var featureDependencyIds = manifestInfo.ModuleInfo.Dependencies .Select(e => e.Trim()).ToArray(); if (!int.TryParse(manifestInfo.ModuleInfo.Priority, out int featurePriority)) { featurePriority = 0; } var featureCategory = manifestInfo.ModuleInfo.Category; var featureDescription = manifestInfo.ModuleInfo.Description; var featureDefaultTenantOnly = manifestInfo.ModuleInfo.DefaultTenantOnly; var featureIsAlwaysEnabled = manifestInfo.ModuleInfo.IsAlwaysEnabled; var context = new FeatureBuildingContext { FeatureId = featureId, FeatureName = featureName, Category = featureCategory, Description = featureDescription, ExtensionInfo = extensionInfo, ManifestInfo = manifestInfo, Priority = featurePriority, FeatureDependencyIds = featureDependencyIds, DefaultTenantOnly = featureDefaultTenantOnly, IsAlwaysEnabled = featureIsAlwaysEnabled }; foreach (var builder in _featureBuilderEvents) { builder.Building(context); } var featureInfo = new FeatureInfo( context.FeatureId, context.FeatureName, context.Priority, context.Category, context.Description, context.ExtensionInfo, context.FeatureDependencyIds, context.DefaultTenantOnly, context.IsAlwaysEnabled); foreach (var builder in _featureBuilderEvents) { builder.Built(featureInfo); } featuresInfos.Add(featureInfo); } return featuresInfos; } }
看起來有點長,其實沒啥說的,基本都在賦值,建構函式通過依賴注入解決,略過。直接看看委託的方法GetFeatures(IExtensionInfo extensionInfo,IManifestInfo manifestInfo),兩個引數就是剛剛那個擴充套件資訊和對應的mainfest.cs所反射的類。建立要返回的功能列表資訊集合var featuresInfos = new List<IFeatureInfo>()。然後獲取該模組依賴的功能集合var features = manifestInfo.ModuleInfo.Features.ToList(),官網也說了一個模組可以有0到多個功能,具體就是Manifest.cs檔案有沒多個assembly: Feature,具體程式碼0個和多個並沒有多大區別,你可以發現多個了用foreach把每個功能加入集合,0個就用擴充套件資訊填充功能然後加入集合返回。這些沒啥問題,然後之前好像注入了什麼featureBuilderEvents在這裡居然有兩個方法Building和Built,它們實現的類是ThemeFeatureBuilderEvents(繼承FeatureBuilderEvents,依賴注入不說也懂,直接找實現就對了,我最怕Events了,我最不瞭解事件,說是委託的封裝,委託我直接理解成指標函式,事件這個概念我還沒想好代替的說辭,其實我都是簡單的理解成多個委託,畢竟事件和委託的區別就是事件可以有多個方法嘛,不對多個委託,我c#都是自學,這些概念可能表述不清楚,上學科班只教了c和java裡面都沒這種概念,我是計算機系但是非軟體專業而是網路專業,見諒)。重寫了方法Building,就是提取出主題啊(判斷是否是主題),(四)這篇說過,模組包含三個解決方案資料夾裡的專案:普通模組、cms模組和主題模組(可能上篇表述不一樣)。下面程式碼很明顯就是有基礎主題就修改下:
public class ThemeFeatureBuilderEvents : FeatureBuilderEvents { public override void Building(FeatureBuildingContext context) { var moduleInfo = context.ExtensionInfo.Manifest.ModuleInfo; if (moduleInfo is ThemeAttribute || (moduleInfo is ModuleMarkerAttribute && moduleInfo.Type.Equals("Theme", StringComparison.OrdinalIgnoreCase))) { var extensionInfo = new ThemeExtensionInfo(context.ExtensionInfo); if (extensionInfo.HasBaseTheme()) { context.FeatureDependencyIds = context .FeatureDependencyIds .Concat(new[] { extensionInfo.BaseTheme }) .ToArray(); } context.ExtensionInfo = extensionInfo; } } }
感覺第(四)篇瞭解過後沒啥說的,後面直接跟蹤就好了,是我的角度不對?怪不得別人都從架構說起,我從asp.net core按流程說中間少了點什麼,有點不連貫,老是得去補充點什麼,越說越亂,被我弄亂的感覺還不如直接自己追蹤下原始碼?我看看能不能努力寫到下箇中間件。要我從架構上說我辦不到啊,我連畫uml的軟體都不知道有哪些,後面學學