我相信 Akka.net 使用 HOCON 的原因之一是因为它是 Akka (Java) 的 1-1 端口,它也严重依赖 HOCON 进行配置。对于可移植性,它是配置框架的首选格式。虽然这只是我的猜测,但它可能是为什么不支持 IConfiguration 的优先事项,因为当前的配置方式“正常工作”,即使它与当今较新的 .NET 应用程序的编写方式不匹配。
有几种方法可以从IConfiguration 实例构建Akka.Configuration.Config。一种方法是获取IConfiguration 对象,并从中构造一个JSON 字符串,然后将其提供给支持解析JSON 的ConfigurationFactory.ParseString。然后可以从解析的Config 实例中获得 HOCON 表示。 为了正确解析 JSON 对象,必须再次解析生成的 HOCON 字符串(我的猜测是因为一个错误导致它以不同方式解释 JSON 和 HOCON)。 确保 JSON 配置不包含任何属性,例如 {"foo.don" : "bar"},而 HOCON 支持解析 foo.don - JSON 不支持。下面是我为从IConfiguration 实例解析Config 而整理的扩展方法:
public static class ConfigurationExtensions
{
public static Config ToAkkaConfiguration(this IConfiguration config)
{
var jToken = BuildJToken(config);
var jsonString = JsonConvert.SerializeObject(jToken);
var hocon = ConfigurationFactory.ParseString(jsonString);
return hocon;
}
// Based on https://stackoverflow.com/a/62533775/1924825
private static JToken BuildJToken(IConfiguration config)
{
var obj = new JObject();
foreach (var child in config.GetChildren())
{
if (child.Path.EndsWith(":0"))
{
var arr = new JArray();
foreach (var arrayChild in config.GetChildren())
{
arr.Add(BuildJToken(arrayChild));
}
return arr;
}
else
{
obj.Add(child.Key, BuildJToken(child));
}
}
if (!obj.HasValues && config is IConfigurationSection section)
{
if (long.TryParse(section.Value, out long integer))
{
return new JValue(integer);
}
else if (bool.TryParse(section.Value, out bool boolean))
{
return new JValue(boolean);
}
else if (decimal.TryParse(section.Value, out decimal real))
{
return new JValue(real);
}
return new JValue(section.Value);
}
return obj;
}
}
更好的解决方案是实现一个解析器,类似于Akka.Configuration.Hocon.Parser 构建Config 对象的方式。这将是比上面的 hacky 方法更好的解决方案:
public static class ConfigurationExtensions
{
public static Config ToAkkaConfiguration(this IConfiguration config)
{
var root = new HoconValue();
ParseConfig(root, config);
var hconRoot = new HoconRoot(root);
return new Config(hconRoot);
}
private static void ParseConfig(HoconValue owner, IConfiguration config)
{
if (config is IConfigurationSection section && !config.GetChildren().Any())
{
var lit = new HoconLiteral { Value = section.Value };
owner.AppendValue(lit);
}
else
{
foreach (var child in config.GetChildren())
{
if (child.Path.EndsWith(":0"))
{
var array = ParseArray(config);
owner.AppendValue(array);
return;
}
else
{
if (owner.GetObject() == null)
owner.NewValue(new HoconObject());
var key = owner.GetObject().GetOrCreateKey(child.Key);
ParseConfig(key, child);
}
}
}
}
private static HoconArray ParseArray(IConfiguration config)
{
var arr = new HoconArray();
foreach (var arrayChild in config.GetChildren())
{
var value = new HoconValue();
ParseConfig(value, arrayChild);
arr.Add(value);
}
return arr;
}
}
在任何一种情况下,都可以使用上述任何一种方法解析具有包含 Akka 配置的元素的 appsettings.json。例如:
{
"AkkaConfiguration": {
"akka": {
"actor": {
"provider": "cluster"
},
"remote": {
"dot-netty": {
"tcp": {
"port": 8081,
"hostname": "localhost"
}
}
},
"cluster": {
"seed-nodes": [ "akka.tcp://test@localhost:8081" ],
"roles": [ "myService" ]
}
}
}
}
可以通过以下方式检索配置的位置:
var config = config.GetSection("AkkaConfiguration").ToAkkaConfiguration();