I have a class that holds my configuration values:

public class DummyConfiguration
    {
        public List<DummyConfigurationObject> DummyConfigurationCollection { get; set; } =
            new List<DummyConfigurationObject>
            {
                new DummyConfigurationObject
                {
                    DummyField = "DummyConfigurationObject1",
                },
                new DummyConfigurationObject
                {
                    DummyField = "DummyConfigurationObject2",
                },
            };
    }

I use auto-initialized properties to provide some reasonable default that works for me.

And here is my appsettings.yaml:

DummyConfigurationCollection:
    - DummyField: DummyConfigurationObject3
    - DummyField: DummyConfigurationObject4
    - DummyField: DummyConfigurationObject5

When I call configuration.Get().DummyConfigurationCollection.Count I'd get 5 cause collection is already initialized and default binder just uses Add method to add additional entries. But what I want instead is to completely replace my default if there are some values in my configuration file that correspond to this property.

Is this possible with default configuration binder?

1

There are 1 answers

7
Pablo Recalde On BEST ANSWER

Yes, what you can do is configure your application like this:

services.Configure<DummyConfiguration>(options => {
 DummyConfiguration.DummyConfigurationCollection.Clear();
 return Configuration.GetSection("DummyConfiguration").Bind(options)
});

It's a overload of Configure which allows you to configure how the binding is being done. In this case we're cleaning all entries on the resulting options instance prior to binding the actual configuration values.

Later you can use this on any other service with IOptions<DummyConfiguration> and it will show just the configured values, not the default ones.

Here's a repo on github with my testing: https://github.com/bison92/IConfigurationCollectionBinding/tree/c29534f41cf07ea22107493d45999fe546462406

EDIT

As you want to use the Configuration.Get() there's no way to modify its behavior, any other than specifying if you want to try to bind private properties or not AFAIK.

So you might want to try implementing your custom ICollection for instance back it with 2 lists (defaults, currents) and override its methods so it responds with currents or defaults but always modifies the currents.

public class DefaultBackedCollection<T> : ICollection<T>
{
    private readonly IList<T> defaults;
    private readonly IList<T> currents = new List<T>();
    public DefaultBackedCollection(List<T> defaultElements)
    {
        defaults = defaultElements ?? new List<T>();
    }
    public int Count => currents.Any() ? currents.Count : defaults.Count;

    public bool IsReadOnly => false;

    public void Add(T item)
    {
        currents.Add(item);
    }

    public void Clear()
    {
        currents.Clear();
    }

    public bool Contains(T item)
    {
        return currents.Any() ? currents.Contains(item) : defaults.Contains(item);
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        if (currents.Any())
        {
            currents.CopyTo(array, arrayIndex);
        }
        else 
        {
            defaults.CopyTo(array, arrayIndex); 
        }
    }

    public IEnumerator<T> GetEnumerator()
    {
        return currents.Any() ? currents.GetEnumerator() : defaults.GetEnumerator();
    }

    public bool Remove(T item) => currents.Remove(item);

    IEnumerator IEnumerable.GetEnumerator()
    {
        return currents.Any() ? currents.GetEnumerator() : defaults.GetEnumerator();
    }
}

Take a look at the updated working example: https://github.com/bison92/IConfigurationCollectionBinding