如何从配置服务模块访问 appsettings.json

How to access appsettings.json from module Configure Services

我有一个包含 appsettings.json

的 Blazor 服务器端解决方案

我正在应用程序项目的应用程序模块中的 ConfigureServices 覆盖中配置 blob 存储。它目前有一个硬编码的连接字符串,并且运行良好。

现在我想将连接字符串移动到 Blazor 项目中的 appsettings.json 文件。

我尝试将 IConfiguration 注入到 ApplicationModule 的构造函数中,但是当我尝试这样做时应用程序抛出错误。

我搜索了传递给 ConfigureServices 覆盖的 ServiceConfigurationContext。有一项服务 属性 包含大约 1,024 个 ServiceDescriptors 的集合,并在 ServiceType.FullName 中找到了一个包含单词 IConfiguration 的服务,但一直无法弄清楚如何使用它来获取服务本身为了获得 appsettings.json 值。

任何人都可以阐明如何从应用程序模块访问 appsettings.json 值吗?

这是我正在使用的代码

namespace MyApp
{
    [DependsOn(
        typeof(MyAppDomainModule),
        typeof(AbpAccountApplicationModule),
        typeof(MyAppApplicationContractsModule),
        typeof(AbpIdentityApplicationModule),
        typeof(AbpPermissionManagementApplicationModule),
        typeof(AbpTenantManagementApplicationModule),
        typeof(AbpFeatureManagementApplicationModule),
        typeof(AbpSettingManagementApplicationModule),
        typeof(AbpBlobStoringModule),
        typeof(AbpBlobStoringAzureModule)
        )]
    public class MyAppApplicationModule : AbpModule
    {
        public override void ConfigureServices(ServiceConfigurationContext context)
        {
            Configure<AbpBlobStoringOptions>(options =>
            {
              options.Containers.ConfigureDefault(container =>
              {
                container.UseAzure(azure =>
                {
                  azure.ConnectionString = "DefaultEndpointsProtocol=https;AccountName=MyApplocalsa;AccountKey=<truncated-account-key>;EndpointSuffix=core.windows.net";
                  azure.ContainerName = "Pictures";
                  azure.CreateContainerIfNotExists = true;
                });
              });
            });
        }
    }
}

This answer has been update based on new information in the question.

如果我对上下文的理解正确,那么您正在 MyAppApplicationModule 中构建您自己的 DI 服务容器。由于我没有关于 MyAppApplicationModule 的足够详细信息,我将演示如何在 OwningComponentBase 的上下文中获取应用程序配置数据,它还定义了它自己的 DI 服务容器。注意我这里用的是Net6.0

首先是web项目appsettings.json中的配置数据

{
  "AzureData": {
    "ConnectionString": "DefaultEndpointsProtocol=https;AccountName=MyApplocalsa;AccountKey=<truncated-account-key>;EndpointSuffix=core.windows.net",
    "ContainerName":  "Pictures"
  },

  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

接下来定义一个数据class来保存配置数据

public class AzureData
{
    public readonly Guid Id = Guid.NewGuid();
    public string ConnectionString { get; set; } = string.Empty;
    public string ContainerName { get; set; } = string.Empty;
}

现在注册一个配置实例,将 AzureData 实例绑定到配置文件中的一个部分。

// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddSingleton<WeatherForecastService>();
builder.Services.Configure<AzureData>(builder.Configuration.GetSection("AzureData"));

最后是组件。

注:

  1. 我们使用IOptions<AzureData>获取具体配置实例,Value获取实际对象
  2. AzureData 是同一个 DI 对象,在本地服务容器内部或外部。它被定义为单例。
@page "/di"
@inherits OwningComponentBase
@using Microsoft.Extensions.Options

<h3>DI Component</h3>
<div class="m-2 p-2">
    Main Service Container <br />
    Id: @AzureDataConfig?.Value.Id <br />
    Connection String: @AzureDataConfig?.Value.ConnectionString
</div>
<div class="m-2 p-2">
    Component Service Container <br />
    Id:@azureData?.Value.Id <br />
    Connection String: @azureData?.Value.ConnectionString
</div>

@code {

    [Inject] private IOptions<AzureData>? AzureDataConfig { get; set; }

    private IOptions<AzureData>? azureData;

    protected override void OnInitialized()
    {
        azureData = ScopedServices.GetService<IOptions<AzureData>>();   
        base.OnInitialized();
    }
}

通过查看解决方案中的其他模块,我终于找到了问题的答案。

这是更新后的代码

namespace MyApp
{
    [DependsOn(
        typeof(MyAppDomainModule),
        typeof(AbpAccountApplicationModule),
        typeof(MyAppApplicationContractsModule),
        typeof(AbpIdentityApplicationModule),
        typeof(AbpPermissionManagementApplicationModule),
        typeof(AbpTenantManagementApplicationModule),
        typeof(AbpFeatureManagementApplicationModule),
        typeof(AbpSettingManagementApplicationModule),
        typeof(AbpBlobStoringModule),
        typeof(AbpBlobStoringAzureModule)
        )]
    public class MyAppApplicationModule : AbpModule
    {
        public override void ConfigureServices(ServiceConfigurationContext context)
        {
            var configuration = context.Services.GetConfiguration();

            Configure<AbpAutoMapperOptions>(options =>
            {
                options.AddMaps<MyAppApplicationModule>();
            });

            Configure<AbpBlobStoringOptions>(options =>
            {
              options.Containers.ConfigureDefault(container =>
              {
                container.UseAzure(azure =>
                {
                  azure.ConnectionString = configuration.GetSection("BlobStorage:ConnectionString").Value;
                  azure.ContainerName    = configuration.GetSection("BlobStorage:ContainerName").Value;
                  azure.CreateContainerIfNotExists = true;
                });
              });
            });
        }
    }
}

我需要添加使用

using Microsoft.Extensions.DependencyInjection;

我能够获得对配置的引用

var configuration = context.Services.GetConfiguration();

我更新了硬编码的连接字符串,从配置中检索它。

azure.ConnectionString = configuration.GetSection("BlobStorage:ConnectionString").Value;
azure.ContainerName    = configuration.GetSection("BlobStorage:ContainerName").Value;

我更新了 Blazor 应用程序中的 appsettings.json 文件

"BlobStorage": {
    "ConnectionString": "DefaultEndpointsProtocol=https;AccountName=myapplocalsa;AccountKey=<truncated>;EndpointSuffix=core.windows.net",
    "ContainerName" :  "Pictures"
}

就是这样!

感谢 Joe 花时间回答我的问题!

对于可能正在寻找同一问题的解决方案的其他人 - 我有几件事要补充。我正在使用一个单独的租户,其中包含单独的 Product.IdentityServerProduct.WebProduct.HttpApi.Host 项目。

我尝试执行的配置是针对 AbpTwilioSmsModuleAbpBlobStoringModule。这些模块的值被硬编码到我的 Product.Domain.ProductDomainModule class.

    // TODO - Need to configure this through appsettings.json - JLavallet 2022-02-10 12:23  
    Configure<AbpTwilioSmsOptions>(options =>
    {
      options.AccountSId = "yada-yada-yada";
      options.AuthToken = "yada-yada-yada";
      options.FromNumber = "+18885551212";
    });
    // TODO - Need to configure this through appsettings.json  - JLavallet 2022-02-10 12:24 
    Configure<AbpBlobStoringOptions>(options =>
    {
      options.Containers.ConfigureDefault(container =>
      {
        container.IsMultiTenant = true;
        container.UseFileSystem(fileSystem => { fileSystem.BasePath = @"D:\Product\DevFiles"; });
      });
    });

我修改了该代码以尝试像 OP 一样从上下文中读取。我不确定上下文的 属性 包含配置。我尝试了各种方法并设置断点来尝试在上下文中找到配置对象,但没有成功。

        Configure<AbpTwilioSmsOptions>(options =>
        {
          options.AccountSId = context.WHAT?["AbpTwilioSms:AccountSId"]; 
          options.AuthToken = context.WHAT?["AbpTwilioSms:AuthToken"]; 
          options.FromNumber = context.WHAT?["AbpTwilioSms:FromNumber"]; 
        });

        Configure<AbpBlobStoringOptions>(options =>
        {
          options.Containers.ConfigureDefault(container =>
          {
            container.IsMultiTenant = Convert.ToBoolean(context.WHAT?["AbpBlobStoring:IsMultiTenant"]);
            container.UseFileSystem(fileSystem =>
            {
              fileSystem.BasePath = context.WHAT?["AbpBlobStoring:FileSystemBasePath"];
            });
          });
        });

那时我遇到了这个 post 并找到了如何从上下文中获取配置对象。

并非一切都很好,但是……

很长一段时间我都不明白为什么我无法读取我放在 Product.HttpApi.Host 根文件夹中的 appsettings.json 配置信息。我能够访问配置对象,但我的值仍然是 null.

然后我想到我应该在我的 Product.Domain 根文件夹中添加一个 appsettings.json 文件;令人惊讶的是没有效果。

我终于开始将服务配置代码从我的 Product.Domain.ProductDomainModule class 移到我的 Product.HttpApi.Host.ProductHttpApiHostModule class 和我的 Product.IdentityServer.ProductIdentityServerModule class.

    [DependsOn(
      typeof(ProductHttpApiModule),
      typeof(AbpAutofacModule),
      typeof(AbpCachingStackExchangeRedisModule),
      typeof(AbpAspNetCoreMvcUiMultiTenancyModule),
      typeof(AbpIdentityAspNetCoreModule),
      typeof(ProductApplicationModule),
      typeof(ProductEntityFrameworkCoreModule),
      typeof(AbpSwashbuckleModule),
      typeof(AbpAspNetCoreSerilogModule)
    )]
    // added by Jack 
    [DependsOn(typeof(AbpTwilioSmsModule))]
    [DependsOn(typeof(AbpBlobStoringModule))]
    [DependsOn(typeof(AbpBlobStoringFileSystemModule))]
    public class ProductHttpApiHostModule : AbpModule
    {
      public override void ConfigureServices(ServiceConfigurationContext context)
      {
        var configuration = context.Services.GetConfiguration();
        var hostingEnvironment = context.Services.GetHostingEnvironment();

        ConfigureUrls(configuration);
        ConfigureConventionalControllers();
        ConfigureAuthentication(context, configuration);
        ConfigureSwagger(context, configuration);
        ConfigureCache(configuration);
        ConfigureVirtualFileSystem(context);
        ConfigureDataProtection(context, configuration, hostingEnvironment);
        ConfigureCors(context, configuration);
        ConfigureExternalProviders(context);
        ConfigureHealthChecks(context);
        ConfigureTenantResolver(context, configuration);
    
        //added by Jack 
        ConfigureTwilioSms(configuration);
        ConfigureBlobStoring(configuration);
      }
    
      private void ConfigureBlobStoring(IConfiguration configuration)
      {
        Configure<AbpBlobStoringOptions>(options =>
        {
          options.Containers.ConfigureDefault(container =>
          {
            container.IsMultiTenant = Convert.ToBoolean(configuration["AbpBlobStoring:IsMultiTenant"]);
            container.UseFileSystem(fileSystem =>
            {
              fileSystem.BasePath = configuration["AbpBlobStoring:FileSystemBasePath"];
            });
          });
        });
      }
    
      private void ConfigureTwilioSms(IConfiguration configuration)
      {
        Configure<AbpTwilioSmsOptions>(options =>
        {
          options.AccountSId = configuration["AbpTwilioSms:AccountSId"]; 
          options.AuthToken = configuration["AbpTwilioSms:AuthToken"]; 
          options.FromNumber = configuration["AbpTwilioSms:FromNumber"]; 
        });
      }

然后我将我的配置条目从 Product.HttpApi.Host\appsettings.json 文件复制到我的 Product.IdentityServer\appsettings.json 文件中,一切都很顺利。

    {
      ...,
      "AbpTwilioSms": {
        "AccountSId": "yada-yada-yada",
        "AuthToken": "yada-yada-yada",
        "FromNumber": "+18885551212"
      },
      "AbpBlobStoring": {
        "IsMultiTenant": true,
        "FileSystemBasePath": "D:\Product\DevFiles\"
      }
    }