【问题标题】:Configuration String with Null DefaultValue具有 Null DefaultValue 的配置字符串
【发布时间】:2018-04-03 17:28:29
【问题描述】:

我有以下 ConfigurationProperty 作为元素的一部分:

[ConfigurationProperty("example", IsRequired = false, DefaultValue = null)]
public string Example { 
    get { return (string)base["example"]; }
    set { base["example"] = value; }
}

如果我按如下方式设置,它将采用"Hello" 字符串并正常工作:

<myElement example="Hello"/>

如果它不存在,我会遇到问题:

<myElement/>

它没有采用上面指定的默认值null,而是采用String.Empty。为什么会这样,我怎样才能让它采用默认值null

更新

肯定是因为base["example"] 返回String.Empty,其中baseConfigurationElement(索引器在此处定义:https://msdn.microsoft.com/en-us/library/c8693ks1(v=vs.110).aspx),但我仍然不确定为什么它不具有值null.

更新

即使DefaultValue = default(string) 将字符串设置为String.Empty

更新

如果配置中不存在该属性,即使 base.Properties.Contains("example") 也会返回 true

【问题讨论】:

  • 我不知道为什么 DefaultValue 不被尊重,但是您是否尝试通过检查 base.Properties.Contains("example") 来扩展属性的 getter,如果为 false,则手动返回 null
  • @stakx 感谢您的想法。你不会相信这一点,但即使 base.Properties.Contains("example") 在配置中不存在 String 属性时也会返回 true

标签: c# .net string configuration app-config


【解决方案1】:

Reference Source for the ConfigurationProperty class来看,这或许不是bug,而是一个特性。

这里是相关的内部方法,InitDefaultValueFromTypeInfo(我做了一些小的格式修改):

private void InitDefaultValueFromTypeInfo(ConfigurationPropertyAttribute attribProperty,
                                          DefaultValueAttribute attribStdDefault) {
     object defaultValue = attribProperty.DefaultValue;

     // If there is no default value there - try the other attribute ( the clr standard one )
     if ((defaultValue == null || defaultValue == ConfigurationElement.s_nullPropertyValue) &&
         (attribStdDefault != null)) {
         defaultValue = attribStdDefault.Value;
     }

     // If there was a default value in the prop attribute - check if we need to convert it from string
     if ((defaultValue != null) && (defaultValue is string) && (_type != typeof(string))) {
         // Use the converter to parse this property default value
         try {
             defaultValue = Converter.ConvertFromInvariantString((string)defaultValue);
         }
         catch (Exception ex) {
             throw new ConfigurationErrorsException(SR.GetString(SR.Default_value_conversion_error_from_string, _name, ex.Message));
         }
     }

     if (defaultValue == null || defaultValue == ConfigurationElement.s_nullPropertyValue) {
         if (_type == typeof(string)) {
             defaultValue = String.Empty;
         }
         else if (_type.IsValueType) {
             defaultValue = TypeUtil.CreateInstanceWithReflectionPermission(_type);
         }
     }

     SetDefaultValue(defaultValue);
 }

最后一个if 块很有趣:如果您的属性类型为string,并且默认值为null,那么默认值将更改为string.Empty

第一个if 块暗示了对这种特殊行为的可能解释。 [ConfigurationProperty] 属性的 DefaultValue 属性是可选的。如果程序员未设置DefaultValue,则默认为null。第一个if 块使用默认的null 来检查是否指定了DefaultValue。如果没有,它会退回到从 [DefaultValue] 属性中获取默认值(如果存在)。

这一切都意味着:指定DefaultValue = null 与根本不指定它具有相同的效果,在这种情况下,配置子系统会为字符串选择一个“正常”的默认值:空字符串。

解决方法:

这是一个有点老套的解决方法:不要将您的配置属性声明为string,而是将其声明为字符串周围的薄包装类型;然后声明一个合适的类型转换器:

[ConfigurationProperty("name", IsRequired = false)]
[TypeConverter(typeof(IncognitoStringConverter))]  // note: additional attribute!
public IncognitoString Name                        // note: different property type
{
    get
    {
        return (IncognitoString)base["name"];
    }
    set
    {
        base["name"] = value;
    }
}

这里是IncognitoStringIncognitoStringConverter 的实现:

public struct IncognitoString
{
    private IncognitoString(string value)
    {
        this.value = value;
    }

    private readonly string value;

    public static implicit operator IncognitoString(string value)
    {
        return new IncognitoString(value);
    }

    public static implicit operator string(IncognitoString incognitoString)
    {
        return incognitoString.value;
    }

    … // perhaps override ToString, GetHashCode, and Equals as well.
}

public sealed class IncognitoStringConverter : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return sourceType == typeof(string);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        return (IncognitoString)(string)value;
    }
}

因为IncognitoString 可以隐式转换为string,所以您可以将属性值分配给任何字符串变量。我知道,仅仅为了获得可为空的属性就很麻烦而且真的很复杂。也许只是忍受空字符串。

【讨论】:

  • 我在 2014 到 2015 年看到的任何问题的最佳答案! :D
【解决方案2】:

另一种解决方法是像这样拨打电话:

[ConfigurationProperty("Prompt")]
public string Prompt
{
    get { return this.GetNullableStringValue("Prompt"); }
}

private string GetNullableStringValue(string propertyName)
{
    return (string)this[new ConfigurationProperty(propertyName, typeof(string), null)];
}

像这样调用GetNullableString 会绕过配置属性属性并阻止它默认DefaultValue 为null。您也可以将方法放在基类中以使其更整洁。

如果你想改变默认值,你只需要记住你正在调用它。

如果您想解除您可能在属性上定义的其他一些内容,您也可以调用this.ElementInformation.Properties[propertyName] - 只是不要使用它来填充DefaultValue

【讨论】:

  • 谢谢,太好了。
【解决方案3】:

不用检查null 的属性值,您可以轻松地检查该属性是否已在配置文件中设置或是否已返回默认值。这可以通过查看ConfigurationElementElementInformation 中的ValueOrigin 来完成。

// if not the default value...    
if (MyConfigurationElement.ElementInformation.Properties["example"].ValueOrigin!=
        PropertyValueOrigin.Default)
{
    ...
}

另请参阅PropertyValueOrigin Enumeration 值的文档。

【讨论】:

    【解决方案4】:

    ConfigurationElement 类型具有 ElementInformation 属性,而 IsPresent 属性又具有 IsPresent 属性。

    因此,与其尝试返回 null ConfigurationElement,不如检查 IsPresent 属性以查看“关联的 ConfigurationElement 对象是否在配置文件中”。 1

    例如:

    if (Configuration.Example.ElementInformation.IsPresent)
    {
        ...
    }
    

    【讨论】:

    • 这是最好的答案,应该标记为解决方案
    【解决方案5】:

    我选择使用更具可读性和可重用性的扩展方法ToNullIfEmpty()。我将 DefaultValue 保留在适当的位置,以防将 null 字符串转换为 String.Empty 的非直观行为发生变化。

    [ConfigurationProperty("dataCenterRegion", DefaultValue = null)]
    public string DataCenterRegion
    {
        get { return ((string)this["dataCenterRegion"]).ToNullIfEmpty(); }
        set { this["dataCenterRegion"] = value; }
    }
    
    public static partial class ExtensionMethods
    {        
        /// <summary>
        /// Return null if the string is empty or is already null.
        /// Otherwise, return the original string.
        /// </summary>
        public static string ToNullIfEmpty(this string str)
        {
            return String.IsNullOrEmpty(str) ? null : str;
        }
    
        /// <summary>
        /// Return null if the string is white space, empty or is already null.
        /// Otherwise, return the original string.
        /// </summary>
        public static string ToNullIfWhiteSpaceOrEmpty(this string str)
        {
            return String.IsNullOrWhiteSpace(str) ? null : str;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2021-12-26
      • 2017-02-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多