ConfigurationValidatorBase validate method receive

2019-07-25 17:05发布

问题:

I am trying to build a FloatValidatorAttribute. In this msdn article: https://msdn.microsoft.com/en-us/library/system.configuration.configurationvalidatorattribute(v=vs.110).aspx

there are some examples. The "ProgrammableValidator" and its attribute example is what I want for a float validator.

The only relevant thing I can find on this site is this unanswered question: Validation of double using System.Configuration validator

I also found this: https://social.msdn.microsoft.com/Forums/vstudio/en-US/6faf9c70-162c-499b-8d0c-0b1f19c7a24a/issues-with-custom-configuration-validator-and-attribute?forum=clr That person has what sounds like a similar problems as I do. But it wasn't helpful for me

My issue is that the value from the web.config is not properly passed to the Validate method of the FloatValidator I created.

Here is my code:

class FloatValidator : ConfigurationValidatorBase
{
    public float MinValue { get; private set; }
    public float MaxValue { get; private set; }

    public FloatValidator(float minValue, float maxValue)
    {
        MinValue = minValue;
        MaxValue = maxValue;
    }

    public override bool CanValidate(Type type)
    {
        return type == typeof(float);
    }

    public override void Validate(object obj)
    {
        float value;
        try
        {
            value = float.Parse(obj.ToString());
        }
        catch (Exception)
        {
            throw new ArgumentException();
        }

        if (value < MinValue)
        {
            throw new ConfigurationErrorsException($"Value too low, minimum value allowed: {MinValue}");
        }

        if (value > MaxValue)
        {
            throw new ConfigurationErrorsException($"Value too high, maximum value allowed: {MaxValue}");
        }
    }
}

The attribute it self:

class FloatValidatorAttribute : ConfigurationValidatorAttribute
{
    public float MinValue { get; set; }
    public float MaxValue { get; set; }

    public FloatValidatorAttribute(float minValue, float maxValue)
    {
        MinValue = minValue;
        MaxValue = maxValue;
    }

    public override ConfigurationValidatorBase ValidatorInstance => new FloatValidator(MinValue, MaxValue);
}

The configuration element it self:

public class Compound : ConfigurationElement
{
    [ConfigurationProperty("name", IsRequired = true, IsKey = true)]
    public string Name => this["name"] as string;

    [ConfigurationProperty("abbreviation", IsRequired = true)]
    public string Abbreviation => this["abbreviation"] as string;

    [ConfigurationProperty("id", IsRequired = true)]
    [IntegerValidator(ExcludeRange = false, MinValue = 0, MaxValue = int.MaxValue)]
    public int Id => (int)this["id"];

    [ConfigurationProperty("factor", IsRequired = true)]
    [FloatValidator(float.Epsilon, float.MaxValue)]
    public float Factor => (float) this["factor"];
}

Here's an example of compound elements from the web.config

    <add name="Ozone" abbreviation="O3" id="147" factor="1.9957"/>
    <add name="Particles smaller than 10 µm, Tapered Element Oscillating Microbalance measurement" abbreviation="PM10Teom" id="161" factor="1" />

I can retrieve the values correctly, and I can apply the factor to the measurements that I am working on. But if I apply the FloatValidator all the values passed to Validate() in the class FloatValidator is 0, so I cannot actually validate the input.

Thank you in advance

回答1:

The framework seems to be validating the default value of your property. Since no default value exists, default(float) is used. This is why you are seeing a call to Validate where 0 is passed.

Since your validation fail you do not see the subsequent calls. They would include the relevant values from your configuration.

You should supply a default value for Factor:

[ConfigurationProperty("factor", IsRequired = true, DefaultValue = float.Epsilon)]

The same actually is the case for the built-in IntegerValidator-attribute used for the Id-property. If you use a range that doesn't include zero, and do not apply a default value it will not validate. See https://stackoverflow.com/a/2150643/1668425.



回答2:

It seems to work.

Using this app.config:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="CompoundConfiguration" type="ConsoleApplication2.CompoundConfigurationSection,ConsoleApplication2,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null"  />
  </configSections>  
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
    </startup>
  <CompoundConfiguration>
    <Compounds>
      <add name="Particles smaller than 10 µm, Tapered Element Oscillating Microbalance measurement" abbreviation="PM10Teom" id="161" factor="1" />
      <add name="Ozone" abbreviation="O3" id="147" factor="1.9957" />
    </Compounds>
  </CompoundConfiguration>
</configuration>

And providing an implementation of ConfigurationSection:

public class CompoundConfigurationSection : ConfigurationSection
{
    [ConfigurationProperty("Compounds", IsDefaultCollection = false)]
    [ConfigurationCollection(typeof(CompoundCollection),
        AddItemName = "add",
        ClearItemsName = "clear",
        RemoveItemName = "remove")]
    public CompoundCollection Compounds
    {
        get
        {
            return (CompoundCollection)base["Compounds"];
        }
    }
}

Along with an ElementCollection:

public class CompoundCollection : ConfigurationElementCollection
{
    public CompoundCollection()
    {
    }

    public Compound this[int index]
    {
        get { return (Compound)BaseGet(index); }
        set
        {
            if (BaseGet(index) != null)
            {
                BaseRemoveAt(index);
            }
            BaseAdd(index, value);
        }
    }

    public void Add(Compound serviceConfig)
    {
        BaseAdd(serviceConfig);
    }

    public void Clear()
    {
        BaseClear();
    }

    protected override ConfigurationElement CreateNewElement()
    {
        return new Compound();
    }

    protected override object GetElementKey(ConfigurationElement element)
    {
        return ((Compound)element).Id;
    }

    public void Remove(Compound serviceConfig)
    {
        BaseRemove(serviceConfig.Id);
    }

    public void RemoveAt(int index)
    {
        BaseRemoveAt(index);
    }

    public void Remove(string name)
    {
        BaseRemove(name);
    }
}

Running this main:

    static void Main(string[] args)
    {
        var compounds = ConfigurationManager.GetSection("CompoundConfiguration");
    }

Gives an exception with the message:

Value too low, minimum value allowed: 1,401298E-45

Which I'm guessing is the expected result?