zoukankan      html  css  js  c++  java
  • (六)学习了解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)。

      本来打算做一个系列的,看起来太简单了,没必要,自己设置下断点追踪下。下篇快速过另外一个中间件就结束了,回头看看能不能整理成一个框架图出来,从框架讲应该比源码(太啰嗦还容易乱)好多了,主要我还不会画图是个问题。

  • 相关阅读:
    Dynamics CRM修改密码界面
    Dynamics CRM新加了组织后提示数据加密错误的解决方法
    ADFS修改默认访问端口
    Dynamics CRM与ADFS安装到同一台服务器后ADFS服务与Dynamics CRM沙盒服务冲突提示808端口占用问题
    Dynamics CRM各个版本的元数据浏览解决方案
    Dynamics CRM9.0安装CRM的时候提示Microsoft.Crm.Setup.Server.RegisterSandboxServiceAction操作失败
    IIS误删了默认网站,恢复方法
    grafana配置邮件告警
    Git连接Gitlab并提交内容
    docker自定义bridge
  • 原文地址:https://www.cnblogs.com/ShuiSen/p/13334737.html
Copyright © 2011-2022 走看看