Строка конфигурации с Null DefaultValue
у меня есть следующая 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
, где base
это ConfigurationElement
(индексатор определяется здесь:https://msdn.microsoft.com/en-us/library/c8693ks1 (v=против 110).aspx), но я все еще не уверен, почему он не принимает значение null
.
обновление
даже DefaultValue = default(string)
задает строку String.Empty
.
обновление
даже base.Properties.Contains("example")
возвращает true
если свойство не существует в конфигурации.
5 ответов
исходя из ссылка на источник ConfigurationProperty
класс, это, возможно, не ошибка, но особенность.
вот соответствующий внутренний метод,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
блок намекает на возможное объяснение этого странного поведения. The [ConfigurationProperty]
атрибут DefaultValue
свойство является необязательным. Если DefaultValue
не устанавливается программистом, тогда он по умолчанию . Первый if
блок использует это значение по умолчанию null
чтобы проверить, является ли . Если нет, он возвращается к извлечению значения по умолчанию из [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;
}
}
вот реализации для IncognitoString
и IncognitoStringConverter
:
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
, вы можете присвоить значение свойства любой строковой переменной. Я знаю, это хаки и действительно сложно просто получить nullable свойства. Возможно, просто жить с пустой строкой.
другой обходной путь - сделать вызов примерно так:
[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
вместо проверки значения свойства для null
вы можете легко проверить, установлено ли свойство в файле конфигурации или возвращено значение по умолчанию. Это можно сделать как ValueOrigin
на ConfigurationElement
' s ElementInformation
.
// if not the default value...
if (MyConfigurationElement.ElementInformation.Properties["example"].ValueOrigin!=
PropertyValueOrigin.Default)
{
...
}
см. также документацию по значениям Перечисление PropertyValueOrigin.
на ConfigurationElement
тип имеет ElementInformation
свойство, которое, в свою очередь, имеет IsPresent
собственность.
Итак, вместо того, чтобы пытаться вернуть null ConfigurationElement
, вы можете проверить IsPresent
свойство для просмотра " находится ли Связанный объект ConfigurationElement в файле конфигурации."1
например:
if (Configuration.Example.ElementInformation.IsPresent)
{
...
}
Я решил использовать более читаемый и многоразовый метод расширения ToNullIfEmpty()
. Я оставил значение DefaultValue на месте, если когда-либо произойдет изменение неинтуитивного поведения, которое преобразует нулевые строки в строку.Пустой.
[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;
}
}