我想在我的应用程序中动态加载和注册服务。为此,我需要能够从解决方案中的不同项目加载配置文件,并将它们的值合并到单个 json 数组中。 不幸的是,默认情况下 ASP.Net Core 配置会覆盖值。
我使用以下代码注册文件(Program.cs 文件的一部分):
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((webHostBuilderContext, configurationbuilder) =>
{
var env = webHostBuilderContext.HostingEnvironment;
configurationbuilder.SetBasePath(env.ContentRootPath);
configurationbuilder.AddJsonFile("appsettings.json", false, true);
var path = Path.Combine(env.ContentRootPath, "App_Config\\Include");
foreach(var file in Directory.EnumerateFiles(path, "*.json",SearchOption.AllDirectories))
{
configurationbuilder.AddJsonFile(file, false, true);
}
configurationbuilder.AddEnvironmentVariables();
})
.UseStartup<Startup>();
代码在 App_Config\Include
目录中搜索所有扩展名为 *.json
的文件,并将它们全部添加到配置构建器中。
文件结构如下所示:
{
"ServicesConfiguration": {
"Services": [
{
"AssemblyName": "ParsingEngine.ServicesConfigurator, ParsingEngine"
}
]
}
}
如您所见,我有主要部分 ServicesConfiguration
,然后是 Services
数组,其中包含具有一个属性 AssemblyName
的对象。
要读取这些值,我使用带有列表的 ServicesConfiguration
类:
public class ServicesConfiguration
{
public List<ServiceAssembly> Services { get; set; }
}
并且该列表使用 ServiceAssembly
类:
public class ServiceAssembly
{
public string AssemblyName { get; set; }
}
为了加载该配置,我在构造函数级别 (DI) 使用 IOptions
:
Microsoft.Extensions.Options.IOptions<ServicesConfiguration> servicesConfiguration,
配置似乎已加载 - 但文件中的值未合并,而是被最后找到的文件覆盖。
有什么解决办法吗?
最佳答案
所以您对我在评论中的意思有所了解,这是一个可能的答案
由于您必须从不同的项目加载不同的“配置”文件并对它们应用一些合并逻辑,我会避免使用“默认”配置系统将 JSON
文件加载到应用程序中.相反,我会自己做。所以:
- 读取 JSON 并将其反序列化为一种类型并将其保存在列表中
- 浏览包含所有配置的列表并应用您的合并逻辑
- 将单个
ServicesConfiguration
注册为Singleton
- 删除您在
Program.cs
上的代码以加载自定义JSON
文件
这里是你如何做到的:
ServicesRootConfiguration
(新类,能够反序列化 json)
public class ServicesRootConfiguration
{
public ServicesConfiguration ServicesConfiguration { get; set; }
}
Startup.cs
public class Startup
{
private readonly IHostingEnvironment _hostingEnvironment;
public Startup(IConfiguration configuration, IHostingEnvironment env)
{
Configuration = configuration;
_hostingEnvironment = env;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
// other configuration omitted for brevity
// build your custom configuration from json files
var myCustomConfig = BuildCustomConfiguration(_hostingEnvironment);
// Register the configuration as a Singleton
services.AddSingleton(myCustomConfig);
}
private static ServicesConfiguration BuildCustomConfiguration(IHostingEnvironment env)
{
var allConfigs = new List<ServicesRootConfiguration>();
var path = Path.Combine(env.ContentRootPath, "App_Config");
foreach (var file in Directory.EnumerateFiles(path, "*.json", SearchOption.AllDirectories))
{
var config = JsonConvert.DeserializeObject<ServicesRootConfiguration>(File.ReadAllText(file));
allConfigs.Add(config);
}
// do your logic to "merge" the each config into a single ServicesConfiguration
// here I simply select the AssemblyName from all files.
var mergedConfig = new ServicesConfiguration
{
Services = allConfigs.SelectMany(c => c.ServicesConfiguration.Services).ToList()
};
return mergedConfig;
}
}
然后在您的 Controller
中通常通过 DI 获取实例。
public class HomeController : Controller
{
private readonly ServicesConfiguration _config;
public HomeController(ServicesConfiguration config)
{
_config = config ?? throw new ArgumentNullException(nameof(config));
}
}
使用这种方法,您最终会得到与正常注册 IOptions
相同的行为。但是,您避免了对它的依赖并且不得不使用丑陋的 .Value
(呃呃)。更好的是,您可以将其注册为接口(interface),以便在测试/模拟期间让您的生活更轻松。
关于c# - 如何从 ASP.Net Core 配置文件中合并多个数组?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53985133/