我在 ASP.NET Core 1.1 解决方案中使用配置绑定。基本上,我在“ConfigureServices Startup”部分中有一些用于绑定的简单代码,如下所示:
services.AddSingleton(Configuration.GetSection("SettingsSection").Get<SettingsClass>());
问题在于我的类作为 int 属性,通常绑定到配置文件中的 int 值,但也可以绑定到字符串“disabled”。在幕后,如果该属性绑定到字符串“disabled”,我希望该属性的值为 -1。
它可能比这更复杂,但为了简洁起见,我进行了简化。
我的问题是:如何提供自定义绑定器/转换器来覆盖 SettingsClass 中特定属性的配置绑定,以便在进行字符串转换时它将“禁用”转换为 -1,而不是抛出异常“disabled”无法转换为 Int32?
看来,由于 ConfigurationBinder 使用类型的 TypeDescriptor 来获取转换器,因此我要做的唯一方法就是实现自定义类型转换器并将其插入到我要转换的类的 TypeDescriptor 中到(在本例中为 Int32)。
因此,基本上,在配置发生之前添加此内容:
TypeDescriptor.AddAttributes(typeof(int), new TypeConverterAttribute(typeof(MyCustomIntConverter)));
MyCustomIntConverter 看起来像这样:
public class MyCustomIntConverter : Int32Converter
{
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value != null && value is string)
{
string stringValue = value as string;
if(stringValue == "disabled")
{
return -1;
}
}
return base.ConvertFrom(context, culture, value);
}
}
似乎有些过头了,因为现在应用程序中任何地方的 Int32 的“禁用”都将始终转换为 -1。如果有人知道一种侵入性较小的方法来做到这一点,请告诉我。
我最近偶然发现了同样的问题,并提出了略有不同的解决方案。
我的想法是使用默认的绑定机制。就我而言,我想获取
HashSet
的新实例,其值以 正确的数组格式 存储在数据库中。我创建了一个类,将我的配置绑定到一个在我的配置中命名的 private
属性和一个 public
属性,该属性使用 private
为我创建一个 HashSet
的实例。看起来有点像这样:
// settings.json
{
option: {
ids:[1,2,3],
}
}
和
class
public class Options
{
public HashSet<int> TrueIds
{
get
{
return RestrictedCategoryIds?.ToHashSet();
}
}
private int[] Ids{ get; set; }
}
然后您可以使用
BindNonPublicProperties
确保活页夹将填充您的 private
财产。
// Startup.cs
services.Configure<Options>(Configuration, c => c.BindNonPublicProperties = true);
你说,在你的情况下,这可能不像将“禁用”转换为-1那么简单,但也许我的想法会启发你以不同的方式解决这个问题。
我找到了另一个解决方案,它不一定更好,但它为您提供了一种将其放入扩展方法中的方法。
private const string SectionName = "example";
public static void UseCustomizedSettings(this IServiceCollection services) =>
services
.AddOptions<CustomizedSettings>()
.Configure<IConfiguration>((settings, configuration) =>
{
configuration
.GetRequiredSection(SectionName)
.Bind(settings);
// TODO move to shared project extension method
var numberFlag = configuration
.GetRequiredSection(SectionName)
.GetValue<object?>(nameof(CustomizedSettings.NumberFlag));
settings
.NumberFlag = ParseNumberOrDisabled(numberFlag)!;
});
private static int ParseNumberOrDisabled(object? numberFlag) {
// Conversion logic here
}
现在您可以将 DODO 下面的所有内容移动到共享项目中的扩展方法中,并像这样使用它:
public static void UseCustomizedSettings(this IServiceCollection services) =>
services
.AddOptions<CustomizedSettings>()
.Configure<IConfiguration>((settings, configuration) =>
{
configuration
.GetRequiredSection(SectionName)
.Bind(settings);
configuration
.GetRequiredSection(SectionName)
.BindNumberFlag(nameof(CustomizedSettings.NumberFlag));
});
也许您可以给它一些比
BindNumberFlag
更具描述性的名称,传达它的实际用途,这样任何阅读此配置绑定的人都可以立即看到发生了什么。