Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom Configuration Binder for Property

I'm using Configuration Binding in an ASP.NET Core 1.1 solution. Basically, I have some simple code for the binding in my ConfigureServices Startup section that looks like this:

services.AddSingleton(Configuration.GetSection("SettingsSection").Get<SettingsClass>());

The wrinkle is that my class as an int property that is normally bound to an int value in the configuration file, but could be bound instead to the string "disabled". Under the hood, I want the property to get a value of -1 if it is bound to the string "disabled".

It can be more complicated than this, but I'm simplifying for the sake of brevity.

My question is this: How do I provide a custom binder/converter for that overrides the configuration binding for a specific property in SettingsClass so that when doing a string conversion it will convert "disabled" to -1, rather than throwing an exception that "disabled" can't be converted to an Int32?

like image 662
jceddy Avatar asked Feb 02 '17 16:02

jceddy


2 Answers

It appears that since the ConfigurationBinder uses the type's TypeDescriptor to get the converter, the only way for me to do what I'm trying to do is to implement a custom type converter and insert it into the TypeDescriptor for the class I'm converting to (in this case Int32).

So, basically, add this before configuration happens:

TypeDescriptor.AddAttributes(typeof(int), new TypeConverterAttribute(typeof(MyCustomIntConverter)));

Where MyCustomIntConverter looks something like this:

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);
    }
}

Seems like overkill, as now "disabled" will always convery to -1 for Int32 everwhere in the application. If anyone knows a less invasive way to do this, please let me know.

like image 169
jceddy Avatar answered Oct 18 '22 22:10

jceddy


I just stumbled upon the same problem recently and came up with slightly different solution.

My idea was to use default binding mechanism. In my case I wanted to get new instance of HashSet having values stored in proper array format inside my database. I created a class I'm binding my configuration to with one private property named as in my configuration and one public property, which uses the private one to create me an instance of HashSet. It looks a little like this:

// settings.json
{
    option: {
        ids:[1,2,3],
    }
}

and the class

public class Options
{
    public HashSet<int> TrueIds
    {
        get
        {
            return RestrictedCategoryIds?.ToHashSet();
        }
    }

    private int[] Ids{ get; set; }
}

Then you can use BindNonPublicProperties to ensure that binder will fill your private property.

// Startup.cs
services.Configure<Options>(Configuration, c => c.BindNonPublicProperties = true);

You say that in your case this might be not as simple as converting "disabled" into -1, but maybe my idea will inspire you to solve this one in different way.

like image 22
Paweł Hemperek Avatar answered Oct 18 '22 21:10

Paweł Hemperek