3

I have the following node in my web.config

<parameter value="100" type="System.Int64, mscorlib" />

which is read into the following ConfigurationProperty

public class ParameterElement : ConfigurationElement
{
    [ConfigurationProperty("type", IsRequired = false, DefaultValue = "System.String, mscorlib")]
    [TypeConverter(typeof (TypeNameConverter))]
    public Type Type
    {
        get { return (Type) this["type"]; }
        set { this["type"] = value; }
    }

    [ConfigurationProperty("value", IsRequired = true)]
    public object Value
    {
        get { return ... ? }
        set { this["value"] = value; }
    }
}

This is correctly establishing the Type that I've set on the node, but how can I return the value in that type? Everything that I've tried returns the following exception:

Unable to find a converter that supports conversion to/from string for the property 'value' of type 'Object'.

awj
  • 7,482
  • 10
  • 66
  • 120
  • What the type of the object which returned by `this["value"]`? – Shlomi Borovitz Mar 08 '14 at 17:40
  • The type is to be determined by the Type ConfigurationProperty immediately above it. – awj Mar 08 '14 at 17:40
  • Then what's hold you from returning it? (`return this["value"];`). Can you check the actual type? Or does the call to `this["value"]` is the one which throws the exception? – Shlomi Borovitz Mar 08 '14 at 17:43
  • 1
    When .NET reads configuration properties in the web.config they all exist as strings. Simply writing `return this["value"];` returns a string. What I want is to return the value, as read from the configuration element in the web.config, cast to the type specified in the Type ConfigurationProperty above. – awj Mar 08 '14 at 17:49
  • Two things. The `Convert` class provides many conversions from string (and more), to the builtin types (the `ChangeType` method would do exactly what you need). For other types (and as more general approach), you could use XmlSerializer to desrialize the object (I'll give you an answer for that soon) – Shlomi Borovitz Mar 08 '14 at 18:04
  • I've already tried `return Convert.ChangeType(this["value"], Type)` and I still get the error which I showed in the question. – awj Mar 08 '14 at 18:13
  • @ShlomiBorovitz, With respect, sir, if you don't have an answer or if you're not familiar with ConfigurationProperty objects then please don't 'shoot in the dark' to try and gain some points. You've already deleted one suggested answer which didn't tackle the problem. – awj Mar 08 '14 at 18:15
  • Well, I checked, and `Convert.ChangeType("100", typeof(long))` works fine. So, with respect, sir, I would say that the problem is something else, because the call to `this["value"]` throws the exception, before any conversion could be made. And, because you don't won't a help on how to resolve the problem, and rather want someone will just give you a working solution although you didn't provider enough data (like, where the exception is thrown from) - I'll just wish you good luck, and have a good day. – Shlomi Borovitz Mar 08 '14 at 18:21

1 Answers1

0

It may be too late, but I had the same need and i found a solution.

First you need to add a TypeConverter on Value to deserialize the configuration, I choose StringConverter but we could implement a XMLConverter or a JSONConverter.

Second, you must parse the string, xml or json

[TypeConverter(typeof(StringConverter))]
[ConfigurationProperty(ValueKey, IsRequired = true)]
public object Value
{
    get { return this[ValueKey].ConvertTo(this.Type); }
    set { this[ValueKey] = value; }
}

Here my extension method to parse the string

/// <summary>
/// Convert an object to a specific type with a support of string parsing if needed
/// </summary>
/// <param name="input">Object to convert</param>
/// <param name="type">Type of converted object</param>
/// <returns>Object converted</returns>
public static object ConvertTo(this object input, Type type)
{
    object returnValue;
    try
    {
        // Try change type
        returnValue = System.Convert.ChangeType(input, type);
    }
    catch(Exception exception)
    {
        if(exception is InvalidCastException || exception is FormatException)
        {
            // Try to parse because the cast is invalid
            // If the type is an enumeration
            if(type.IsEnum)
            {
                // Try to parse the string
                try { returnValue = Enum.Parse(type, input.ToString(), true); }
                catch(Exception) { returnValue = System.Convert.ChangeType(input, typeof(int)); }
            }
            else if(type.IsNullable())
            {
                // If the type is a nullable type (int?,long?,double?....)
                returnValue = input == null ? null : System.Convert.ChangeType(input, Nullable.GetUnderlyingType(type));
            }
            else if(input is string || input == null)
            {
                // If the original type is string, we try to parse : if parsing failed then return value is the default value of the type
                if(!((string)input).TryParse(type, out returnValue))
                {
                    // Conversion "1" to true is not supported by previous case, so if return type is boolean try to convert "1" to true
                    if(type == typeof(Boolean))
                        returnValue = ((string)input).ToBoolean();
                }
            }
            else
                throw new InvalidCastException(String.Format("Unable to cast \"{0}\" in {1}", input, type.Name));
        }
        else
            throw;
    }
    // return the value converted
    return returnValue;
}

/// <summary>
/// Try to parse a string
/// </summary>
/// <param name="text">Text to parse</param>
/// <param name="type">Type of result</param>
/// <param name="result">Result</param>
/// <returns>True if string was parsed, else false</returns>
public static bool TryParse(this string text, Type type, out object result)
{
    // Get specific converter for the type
    TypeConverter converter = TypeDescriptor.GetConverter(type);
    // If there is a converter and conversion is valid
    if(converter != null && converter.IsValid(text))
    {
        // Convert
        result = converter.ConvertFromInvariantString(text);
        return true;
    }
    else
    {
        // Return the default value of the type
        result = type.GetDefaultValue();
        return false;
    }
}

/// <summary>
/// Get the default value of a type
/// </summary>
/// <param name="type">Type</param>
/// <returns>Default value</returns>
public static object GetDefaultValue(this Type type)
{
    return type.IsValueType ? Activator.CreateInstance(type) : null;
}

/// <summary>
/// Define if a type is a nullable type (int?, long?, double?...)
/// </summary>
/// <param name="type">Type</param>
/// <returns>true if the type is a nullable type</returns>
public static bool IsNullable(this Type type)
{
    return (!type.IsGenericType) ? false : type.GetGenericTypeDefinition().Equals(typeof(Nullable<>));
}
Troopers
  • 5,127
  • 1
  • 37
  • 64