1. 程式人生 > 實用技巧 >(六)學習瞭解OrchardCore筆記——靈魂中介軟體ModularTenantContainerMiddleware快速過了

(六)學習瞭解OrchardCore筆記——靈魂中介軟體ModularTenantContainerMiddleware快速過了

  這原始碼下面好像沒啥說的,都是簡單的封裝,自己除錯跟蹤下就明白了,猶豫了幾天,講下去感覺沒玩沒了的基礎知識,我準備快速過了。

  上次講到ExtensionManager的擴充套件,往下原始碼就是功能了

          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;

  其實就是為了返回_features,把擴充套件封裝、排序、篩選下,真的沒啥說的,自己跟蹤吧,跳過,返回ShellHost的PreCreateAndRegisterShellsAsync方法。

 

        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");
            }
        }

  這個其實也沒啥好說的,就是載入完功能載入配置,然後沒啟動就啟動(每個租戶都啟動)。這裡說一個我自己的坑就是_shellSettingsManager.LoadSettingsAsync()這個的實現ShellSettingsManager的LoadSettingsAsync方法第一行EnsureConfigurationAsync()這個方法,我們看看這個程式碼

     private async Task EnsureConfigurationAsync()
        {
            if (_configuration != null)
            {
                return;
            }

            await _semaphore.WaitAsync();
            try
            {
                if (_configuration != null)
                {
                    return;
                }

                var lastProviders = (_applicationConfiguration as IConfigurationRoot)?.Providers
                    .Where(p => p is EnvironmentVariablesConfigurationProvider ||
                                p is CommandLineConfigurationProvider)
                    .ToArray();

                var configurationBuilder = await new ConfigurationBuilder()
                    .AddConfiguration(_applicationConfiguration)
                    .AddSourcesAsync(_tenantsConfigSources);

                if (lastProviders.Count() > 0)
                {
                    configurationBuilder.AddConfiguration(new ConfigurationRoot(lastProviders));
                }

                var configuration = configurationBuilder.Build().GetSection("OrchardCore");

                _configuredTenants = configuration.GetChildren()
                    .Where(section => Enum.TryParse<TenantState>(section["State"], ignoreCase: true, out var result))
                    .Select(section => section.Key)
                    .Distinct()
                    .ToArray();

                _configBuilderFactory = async (tenant) =>
                {
                    await _semaphore.WaitAsync();
                    try
                    {
                        var builder = new ConfigurationBuilder().AddConfiguration(_configuration);
                        builder.AddConfiguration(configuration.GetSection(tenant));
                        return await builder.AddSourcesAsync(tenant, _tenantConfigSources);
                    }
                    finally
                    {
                        _semaphore.Release();
                    }
                };

                _configuration = configuration;
            }
            finally
            {
                _semaphore.Release();
            }
        }

  裡面有這個_applicationConfiguration as IConfigurationRoot,我一開始直接找IConfigurationRoot的實現,由於前面跳過服務,不知道這裡的IConfigurationRoot並沒有注入自己的依賴注入,繞了很多彎路,我真是傻啊,IConfigurationRoot是微軟預設的依賴注入,這點注意下就沒啥難度了。

  ShellHost這裡就直接過了,回到中介軟體ModularTenantContainerMiddleware去

  

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初始化了,shellSetting就是之前初始化中的配置讀取對應上下文的設定(多租戶挑啊),加Header什麼的,這個太容易理解了吧,剩下幾行都有註釋了,最後熟悉的asp.net core呼叫下箇中間件的_next.Invoke(httpContext)。

  本來打算做一個系列的,看起來太簡單了,沒必要,自己設定下斷點追蹤下。下篇快速過另外一箇中間件就結束了,回頭看看能不能整理成一個框架圖出來,從框架講應該比原始碼(太囉嗦還容易亂)好多了,主要我還不會畫圖是個問題。