1. 程式人生 > 實用技巧 >(五)學習瞭解OrchardCore筆記——靈魂中介軟體ModularTenantContainerMiddleware的第一行②模組的功能部分

(五)學習瞭解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 > )
{
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 = ;
} 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 = ;
} 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的軟體都不知道有哪些,後面學學看。