(五)學習瞭解OrchardCore筆記——靈魂中介軟體ModularTenantContainerMiddleware的第一行②模組的功能部分

shuisen發表於2020-07-14

  在(三)的時候已經說到模組集合用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的軟體都不知道有哪些,後面學學看。

相關文章