Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
871166c
First pass
SteveDunn Apr 17, 2022
3fe8714
Remove commented code
SteveDunn Apr 17, 2022
65ea989
Support IReadOnlySet. Refactor out method that determines most suitab…
SteveDunn Apr 18, 2022
c2d1038
Handles ISet after rebase. Still to do IReadOnlySet
SteveDunn Apr 19, 2022
2613c44
Add support back for `IReadOnlySet`
SteveDunn Apr 19, 2022
b377ac1
PR feedback - pre-allocate just one array instead of many allocations…
SteveDunn Apr 19, 2022
10e6238
All tests pass.
SteveDunn Apr 22, 2022
c8e7b60
Tidy
SteveDunn Apr 23, 2022
ef33f6f
Skips custom sets
SteveDunn Apr 23, 2022
accbc13
Strip out unused code
SteveDunn Apr 23, 2022
92afd9a
Tidy up and remove dead code.
SteveDunn Apr 23, 2022
3687886
Tidy up tests
SteveDunn Apr 23, 2022
3c65aa4
Tidy up comments
SteveDunn Apr 23, 2022
ee0b40b
Copy over @halter73's changes
SteveDunn May 5, 2022
6b6f6cd
Copy over @halter73's changes
SteveDunn May 5, 2022
6efcd54
Copy over @halter73's changes
SteveDunn May 5, 2022
3454939
Added restrictions and some tests for set keys being string/enum only
SteveDunn May 5, 2022
686192e
PR feedback re. preprocessor directive
SteveDunn May 30, 2022
b9a9f3a
Started on instantiated dictionary feedback
SteveDunn May 30, 2022
32609c8
First pass at cloning dictionary<,>
SteveDunn Jun 4, 2022
1310ebd
Can now bind (overwrite) to an existing concrete dictionary
SteveDunn Jun 4, 2022
da49d5b
Refactor out common code for binding to existing dictionary. Added te…
SteveDunn Jun 4, 2022
3f359f4
Comments and more tests
SteveDunn Jun 4, 2022
c015c01
Add explicit (separate) test for binding to non-instantiated IReadOnl…
SteveDunn Jun 4, 2022
93cf8c0
PR feedback - add tests
SteveDunn Jun 12, 2022
1667f86
PR feedback: introdcue variable for `typeIsADictionaryInterface'
SteveDunn Jun 13, 2022
ca1defe
PR feedback
SteveDunn Jun 13, 2022
72be954
Fix rebase issues
SteveDunn Jul 18, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add support back for IReadOnlySet
  • Loading branch information
SteveDunn committed Jul 18, 2022
commit 2613c44b11375d5267d0b1c9656d008f1ccc9d64
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,16 @@ private static void BindInstance(
return;
}

// for sets and read-only set interfaces, we concatenate on to what is already there
if (IsSet(type))
{
if (!bindingPoint.IsReadOnly)
{
bindingPoint.SetValue(BindSet(type, (IEnumerable?)bindingPoint.Value, config, options));
}
return;
}

// If we don't have an instance, try to create one
if (bindingPoint.Value is null)
{
Expand Down Expand Up @@ -671,6 +681,47 @@ private static Array BindArray(Type type, IEnumerable? source, IConfiguration co
return result;
}

[RequiresUnreferencedCode("Cannot statically analyze what the element type is of the Array so its members may be trimmed.")]
private static object BindSet(Type type, IEnumerable? source, IConfiguration config, BinderOptions options)
{
Type elementType = type.GetGenericArguments()[0];

Type genericType = typeof(HashSet<>).MakeGenericType(type.GenericTypeArguments[0]);
object instance = Activator.CreateInstance(genericType)!;

MethodInfo addMethod = genericType.GetMethod("Add", DeclaredOnlyLookup)!;

if (source != null)
{
foreach (object? item in source)
{
addMethod.Invoke(instance, new[] {item});
}
}

foreach (IConfigurationSection section in config.GetChildren())
{
var itemBindingPoint = new BindingPoint();
try
{
BindInstance(
type: elementType,
bindingPoint: itemBindingPoint,
config: section,
options: options);
if (itemBindingPoint.HasNewValue)
{
addMethod.Invoke(instance, new[] {itemBindingPoint.Value});
}
}
catch
{
}
}

return instance;
}

[RequiresUnreferencedCode(TrimmingWarningMessage)]
private static bool TryConvertValue(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
Expand Down Expand Up @@ -748,6 +799,18 @@ private static bool IsArrayCompatibleReadOnlyInterface(Type type)
|| genericTypeDefinition == typeof(IReadOnlyList<>);
}

private static bool IsSet(Type type)
{
if (!type.IsInterface || !type.IsConstructedGenericType) { return false; }

Type genericTypeDefinition = type.GetGenericTypeDefinition();
return genericTypeDefinition == typeof(ISet<>)
#if NET5_0_OR_GREATER
|| genericTypeDefinition == typeof(IReadOnlySet<>)
#endif
;
}

private static Type? FindOpenGenericInterface(
Type expected,
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
Expand Down Expand Up @@ -53,13 +54,29 @@ public string ReadOnly
public IDictionary<string, ISet<string>> NonInstantiatedDictionaryWithISet { get; set; } = null!;
public IDictionary<string, HashSet<string>> InstantiatedDictionaryWithHashSet { get; set; } =
new Dictionary<string, HashSet<string>>();

public IDictionary<string, HashSet<string>> InstantiatedDictionaryWithHashSetWithSomeValues { get; set; } =
new Dictionary<string, HashSet<string>>
{
{"item1", new HashSet<string>(new[] {"existing1", "existing2"})}
};

public IEnumerable<string> NonInstantiatedIEnumerable { get; set; } = null!;
public ISet<string> InstantiatedISet { get; set; } = new HashSet<string>();
#if NET5_0_OR_GREATER
public ISet<string> InstantiatedISetWithSomeValues { get; set; } =
new HashSet<string>(new[] {"existing1", "existing2"});

#if NET5_0_OR_GREATER
public IReadOnlySet<string> InstantiatedIReadOnlySet { get; set; } = new HashSet<string>();
public IReadOnlySet<string> InstantiatedIReadOnlySetWithSomeValues { get; set; } =
new HashSet<string>(new[] {"existing1", "existing2"});
public IReadOnlySet<string> NonInstantiatedIReadOnlySet { get; set; }
#endif
public IDictionary<string, IReadOnlySet<string>> InstantiatedDictionaryWithReadOnlySetWithSomeValues { get; set; } =
new Dictionary<string, IReadOnlySet<string>>
{
{"item1", new HashSet<string>(new[] {"existing1", "existing2"})}
};
#endif
public IEnumerable<string> InstantiatedIEnumerable { get; set; } = new List<string>();
public ICollection<string> InstantiatedICollection { get; set; } = new List<string>();
public IReadOnlyCollection<string> InstantiatedIReadOnlyCollection { get; set; } = new List<string>();
Expand Down Expand Up @@ -529,6 +546,28 @@ public void CanBindInstantiatedIReadOnlySet()
Assert.Equal("Yo2", options.InstantiatedIReadOnlySet.ElementAt(1));
}

[Fact]
public void CanBindInstantiatedIReadOnlyWithSomeValues()
{
var dic = new Dictionary<string, string>
{
{"InstantiatedIReadOnlySetWithSomeValues:0", "Yo1"},
{"InstantiatedIReadOnlySetWithSomeValues:1", "Yo2"},
};
var configurationBuilder = new ConfigurationBuilder();
configurationBuilder.AddInMemoryCollection(dic);

var config = configurationBuilder.Build();

var options = config.Get<ComplexOptions>()!;

Assert.Equal(4, options.InstantiatedIReadOnlySetWithSomeValues.Count);
Assert.Equal("existing1", options.InstantiatedIReadOnlySetWithSomeValues.ElementAt(0));
Assert.Equal("existing2", options.InstantiatedIReadOnlySetWithSomeValues.ElementAt(1));
Assert.Equal("Yo1", options.InstantiatedIReadOnlySetWithSomeValues.ElementAt(2));
Assert.Equal("Yo2", options.InstantiatedIReadOnlySetWithSomeValues.ElementAt(3));
}

[Fact]
public void CanBindNonInstantiatedIReadOnlySet()
{
Expand All @@ -548,7 +587,35 @@ public void CanBindNonInstantiatedIReadOnlySet()
Assert.Equal("Yo1", options.NonInstantiatedIReadOnlySet.ElementAt(0));
Assert.Equal("Yo2", options.NonInstantiatedIReadOnlySet.ElementAt(1));
}

[Fact]
public void CanBindInstantiatedDictionaryOfIReadOnlySetWithSomeExistingValues()
{
var dic = new Dictionary<string, string>
{
{"InstantiatedDictionaryWithReadOnlySetWithSomeValues:foo:0", "foo-1"},
{"InstantiatedDictionaryWithReadOnlySetWithSomeValues:foo:1", "foo-2"},
{"InstantiatedDictionaryWithReadOnlySetWithSomeValues:bar:0", "bar-1"},
{"InstantiatedDictionaryWithReadOnlySetWithSomeValues:bar:1", "bar-2"},
};
var configurationBuilder = new ConfigurationBuilder();
configurationBuilder.AddInMemoryCollection(dic);

var config = configurationBuilder.Build();

var options = config.Get<ComplexOptions>()!;

Assert.Equal(3, options.InstantiatedDictionaryWithReadOnlySetWithSomeValues.Count);
Assert.Equal("existing1", options.InstantiatedDictionaryWithReadOnlySetWithSomeValues["item1"].ElementAt(0));
Assert.Equal("existing2", options.InstantiatedDictionaryWithReadOnlySetWithSomeValues["item1"].ElementAt(1));

Assert.Equal("foo-1", options.InstantiatedDictionaryWithReadOnlySetWithSomeValues["foo"].ElementAt(0));
Assert.Equal("foo-2", options.InstantiatedDictionaryWithReadOnlySetWithSomeValues["foo"].ElementAt(1));
Assert.Equal("bar-1", options.InstantiatedDictionaryWithReadOnlySetWithSomeValues["bar"].ElementAt(0));
Assert.Equal("bar-2", options.InstantiatedDictionaryWithReadOnlySetWithSomeValues["bar"].ElementAt(1));
}
#endif

[Fact]
public void CanBindNonInstantiatedDictionaryOfISet()
{
Expand Down Expand Up @@ -597,6 +664,33 @@ public void CanBindInstantiatedDictionaryOfISet()
Assert.Equal("bar-2", options.InstantiatedDictionaryWithHashSet["bar"].ElementAt(1));
}

[Fact]
public void CanBindInstantiatedDictionaryOfISetWithSomeExistingValues()
{
var dic = new Dictionary<string, string>
{
{"InstantiatedDictionaryWithHashSetWithSomeValues:foo:0", "foo-1"},
{"InstantiatedDictionaryWithHashSetWithSomeValues:foo:1", "foo-2"},
{"InstantiatedDictionaryWithHashSetWithSomeValues:bar:0", "bar-1"},
{"InstantiatedDictionaryWithHashSetWithSomeValues:bar:1", "bar-2"},
};
var configurationBuilder = new ConfigurationBuilder();
configurationBuilder.AddInMemoryCollection(dic);

var config = configurationBuilder.Build();

var options = config.Get<ComplexOptions>()!;

Assert.Equal(3, options.InstantiatedDictionaryWithHashSetWithSomeValues.Count);
Assert.Equal("existing1", options.InstantiatedDictionaryWithHashSetWithSomeValues["item1"].ElementAt(0));
Assert.Equal("existing2", options.InstantiatedDictionaryWithHashSetWithSomeValues["item1"].ElementAt(1));

Assert.Equal("foo-1", options.InstantiatedDictionaryWithHashSetWithSomeValues["foo"].ElementAt(0));
Assert.Equal("foo-2", options.InstantiatedDictionaryWithHashSetWithSomeValues["foo"].ElementAt(1));
Assert.Equal("bar-1", options.InstantiatedDictionaryWithHashSetWithSomeValues["bar"].ElementAt(0));
Assert.Equal("bar-2", options.InstantiatedDictionaryWithHashSetWithSomeValues["bar"].ElementAt(1));
}

[Fact]
public void CanBindInstantiatedISet()
{
Expand All @@ -617,6 +711,28 @@ public void CanBindInstantiatedISet()
Assert.Equal("Yo2", options.InstantiatedISet.ElementAt(1));
}

[Fact]
public void CanBindInstantiatedISetWithSomeValues()
{
var dic = new Dictionary<string, string>
{
{"InstantiatedISetWithSomeValues:0", "Yo1"},
{"InstantiatedISetWithSomeValues:1", "Yo2"},
};
var configurationBuilder = new ConfigurationBuilder();
configurationBuilder.AddInMemoryCollection(dic);

var config = configurationBuilder.Build();

var options = config.Get<ComplexOptions>()!;

Assert.Equal(4, options.InstantiatedISetWithSomeValues.Count);
Assert.Equal("existing1", options.InstantiatedISetWithSomeValues.ElementAt(0));
Assert.Equal("existing2", options.InstantiatedISetWithSomeValues.ElementAt(1));
Assert.Equal("Yo1", options.InstantiatedISetWithSomeValues.ElementAt(2));
Assert.Equal("Yo2", options.InstantiatedISetWithSomeValues.ElementAt(3));
}

[Fact]
public void CanBindInstantiatedIEnumerableWithItems()
{
Expand Down