@@ -1685,5 +1685,182 @@ public class ExtendedDictionary<TKey, TValue> : Dictionary<TKey, TValue>
16851685 {
16861686
16871687 }
1688+
1689+ private class OptionsWithDifferentCollectionInterfaces
1690+ {
1691+ private static IEnumerable < string > s_instantiatedIEnumerable = new List < string > { "value1" , "value2" } ;
1692+ public bool IsSameInstantiatedIEnumerable ( ) => object . ReferenceEquals ( s_instantiatedIEnumerable , InstantiatedIEnumerable ) ;
1693+ public IEnumerable < string > InstantiatedIEnumerable { get ; set ; } = s_instantiatedIEnumerable ;
1694+
1695+ private static IList < string > s_instantiatedIList = new List < string > { "value1" , "value2" } ;
1696+ public bool IsSameInstantiatedIList ( ) => object . ReferenceEquals ( s_instantiatedIList , InstantiatedIList ) ;
1697+ public IList < string > InstantiatedIList { get ; set ; } = s_instantiatedIList ;
1698+
1699+ private static IReadOnlyList < string > s_instantiatedIReadOnlyList = new List < string > { "value1" , "value2" } ;
1700+ public bool IsSameInstantiatedIReadOnlyList ( ) => object . ReferenceEquals ( s_instantiatedIReadOnlyList , InstantiatedIReadOnlyList ) ;
1701+ public IReadOnlyList < string > InstantiatedIReadOnlyList { get ; set ; } = s_instantiatedIReadOnlyList ;
1702+
1703+ private static IDictionary < string , string > s_instantiatedIDictionary = new Dictionary < string , string > { [ "Key1" ] = "value1" , [ "Key2" ] = "value2" } ;
1704+ public IDictionary < string , string > InstantiatedIDictionary { get ; set ; } = s_instantiatedIDictionary ;
1705+ public bool IsSameInstantiatedIDictionary ( ) => object . ReferenceEquals ( s_instantiatedIDictionary , InstantiatedIDictionary ) ;
1706+
1707+ private static IReadOnlyDictionary < string , string > s_instantiatedIReadOnlyDictionary = new Dictionary < string , string > { [ "Key1" ] = "value1" , [ "Key2" ] = "value2" } ;
1708+ public IReadOnlyDictionary < string , string > InstantiatedIReadOnlyDictionary { get ; set ; } = s_instantiatedIReadOnlyDictionary ;
1709+ public bool IsSameInstantiatedIReadOnlyDictionary ( ) => object . ReferenceEquals ( s_instantiatedIReadOnlyDictionary , InstantiatedIReadOnlyDictionary ) ;
1710+
1711+ private static ISet < string > s_instantiatedISet = new HashSet < string > ( StringComparer . OrdinalIgnoreCase ) { "a" , "A" , "b" } ;
1712+ public ISet < string > InstantiatedISet { get ; set ; } = s_instantiatedISet ;
1713+ public bool IsSameInstantiatedISet ( ) => object . ReferenceEquals ( s_instantiatedISet , InstantiatedISet ) ;
1714+
1715+ #if NETCOREAPP
1716+ private static IReadOnlySet < string > s_instantiatedIReadOnlySet = new HashSet < string > ( StringComparer . OrdinalIgnoreCase ) { "a" , "A" , "b" } ;
1717+ public IReadOnlySet < string > InstantiatedIReadOnlySet { get ; set ; } = s_instantiatedIReadOnlySet ;
1718+ public bool IsSameInstantiatedIReadOnlySet ( ) => object . ReferenceEquals ( s_instantiatedIReadOnlySet , InstantiatedIReadOnlySet ) ;
1719+
1720+ public IReadOnlySet < string > UnInstantiatedIReadOnlySet { get ; set ; }
1721+ #endif
1722+ private static ICollection < string > s_instantiatedICollection = new List < string > { "a" , "b" , "c" } ;
1723+ public ICollection < string > InstantiatedICollection { get ; set ; } = s_instantiatedICollection ;
1724+ public bool IsSameInstantiatedICollection ( ) => object . ReferenceEquals ( s_instantiatedICollection , InstantiatedICollection ) ;
1725+
1726+ private static IReadOnlyCollection < string > s_instantiatedIReadOnlyCollection = new List < string > { "a" , "b" , "c" } ;
1727+ public IReadOnlyCollection < string > InstantiatedIReadOnlyCollection { get ; set ; } = s_instantiatedIReadOnlyCollection ;
1728+ public bool IsSameInstantiatedIReadOnlyCollection ( ) => object . ReferenceEquals ( s_instantiatedIReadOnlyCollection , InstantiatedIReadOnlyCollection ) ;
1729+
1730+ public IReadOnlyCollection < string > UnInstantiatedIReadOnlyCollection { get ; set ; }
1731+ public ICollection < string > UnInstantiatedICollection { get ; set ; }
1732+ public ISet < string > UnInstantiatedISet { get ; set ; }
1733+ public IReadOnlyDictionary < string , string > UnInstantiatedIReadOnlyDictionary { get ; set ; }
1734+ public IEnumerable < string > UnInstantiatedIEnumerable { get ; set ; }
1735+ public IList < string > UnInstantiatedIList { get ; set ; }
1736+ public IReadOnlyList < string > UnInstantiatedIReadOnlyList { get ; set ; }
1737+ }
1738+ [ Fact ]
1739+ public void TestOptionsWithDifferentCollectionInterfaces ( )
1740+ {
1741+ var input = new Dictionary < string , string >
1742+ {
1743+ { "InstantiatedIEnumerable:0" , "value3" } ,
1744+ { "UnInstantiatedIEnumerable:0" , "value1" } ,
1745+ { "InstantiatedIList:0" , "value3" } ,
1746+ { "InstantiatedIReadOnlyList:0" , "value3" } ,
1747+ { "UnInstantiatedIReadOnlyList:0" , "value" } ,
1748+ { "UnInstantiatedIList:0" , "value" } ,
1749+ { "InstantiatedIDictionary:Key3" , "value3" } ,
1750+ { "InstantiatedIReadOnlyDictionary:Key3" , "value3" } ,
1751+ { "UnInstantiatedIReadOnlyDictionary:Key" , "value" } ,
1752+ { "InstantiatedISet:0" , "B" } ,
1753+ { "InstantiatedISet:1" , "C" } ,
1754+ { "UnInstantiatedISet:0" , "a" } ,
1755+ { "UnInstantiatedISet:1" , "A" } ,
1756+ { "UnInstantiatedISet:2" , "B" } ,
1757+ { "InstantiatedIReadOnlySet:0" , "Z" } ,
1758+ { "UnInstantiatedIReadOnlySet:0" , "y" } ,
1759+ { "UnInstantiatedIReadOnlySet:1" , "z" } ,
1760+ { "InstantiatedICollection:0" , "d" } ,
1761+ { "UnInstantiatedICollection:0" , "t" } ,
1762+ { "UnInstantiatedICollection:1" , "a" } ,
1763+ { "InstantiatedIReadOnlyCollection:0" , "d" } ,
1764+ { "UnInstantiatedIReadOnlyCollection:0" , "r" } ,
1765+ { "UnInstantiatedIReadOnlyCollection:1" , "e" } ,
1766+ } ;
1767+
1768+ var configurationBuilder = new ConfigurationBuilder ( ) ;
1769+ configurationBuilder . AddInMemoryCollection ( input ) ;
1770+ var config = configurationBuilder . Build ( ) ;
1771+
1772+ var options = new OptionsWithDifferentCollectionInterfaces ( ) ;
1773+ config . Bind ( options ) ;
1774+
1775+ Assert . True ( 3 == options . InstantiatedIEnumerable . Count ( ) , $ "InstantiatedIEnumerable count is { options . InstantiatedIEnumerable . Count ( ) } .. { options . InstantiatedIEnumerable . ElementAt ( options . InstantiatedIEnumerable . Count ( ) - 1 ) } ") ;
1776+ Assert . Equal ( "value1" , options . InstantiatedIEnumerable . ElementAt ( 0 ) ) ;
1777+ Assert . Equal ( "value2" , options . InstantiatedIEnumerable . ElementAt ( 1 ) ) ;
1778+ Assert . Equal ( "value3" , options . InstantiatedIEnumerable . ElementAt ( 2 ) ) ;
1779+ Assert . False ( options . IsSameInstantiatedIEnumerable ( ) ) ;
1780+
1781+ Assert . Equal ( 1 , options . UnInstantiatedIEnumerable . Count ( ) ) ;
1782+ Assert . Equal ( "value1" , options . UnInstantiatedIEnumerable . ElementAt ( 0 ) ) ;
1783+
1784+ Assert . True ( 3 == options . InstantiatedIList . Count ( ) , $ "InstantiatedIList count is { options . InstantiatedIList . Count ( ) } .. { options . InstantiatedIList [ options . InstantiatedIList . Count ( ) - 1 ] } ") ;
1785+ Assert . Equal ( "value1" , options . InstantiatedIList [ 0 ] ) ;
1786+ Assert . Equal ( "value2" , options . InstantiatedIList [ 1 ] ) ;
1787+ Assert . Equal ( "value3" , options . InstantiatedIList [ 2 ] ) ;
1788+ Assert . True ( options . IsSameInstantiatedIList ( ) ) ;
1789+
1790+ Assert . Equal ( 1 , options . UnInstantiatedIList . Count ( ) ) ;
1791+ Assert . Equal ( "value" , options . UnInstantiatedIList [ 0 ] ) ;
1792+
1793+ Assert . True ( 3 == options . InstantiatedIReadOnlyList . Count ( ) , $ "InstantiatedIReadOnlyList count is { options . InstantiatedIReadOnlyList . Count ( ) } .. { options . InstantiatedIReadOnlyList [ options . InstantiatedIReadOnlyList . Count ( ) - 1 ] } ") ;
1794+ Assert . Equal ( "value1" , options . InstantiatedIReadOnlyList [ 0 ] ) ;
1795+ Assert . Equal ( "value2" , options . InstantiatedIReadOnlyList [ 1 ] ) ;
1796+ Assert . Equal ( "value3" , options . InstantiatedIReadOnlyList [ 2 ] ) ;
1797+ Assert . False ( options . IsSameInstantiatedIReadOnlyList ( ) ) ;
1798+
1799+ Assert . Equal ( 1 , options . UnInstantiatedIReadOnlyList . Count ( ) ) ;
1800+ Assert . Equal ( "value" , options . UnInstantiatedIReadOnlyList [ 0 ] ) ;
1801+
1802+ Assert . True ( 3 == options . InstantiatedIReadOnlyList . Count ( ) , $ "InstantiatedIReadOnlyList count is { options . InstantiatedIReadOnlyList . Count ( ) } .. { options . InstantiatedIReadOnlyList [ options . InstantiatedIReadOnlyList . Count ( ) - 1 ] } ") ;
1803+ Assert . Equal ( new string [ ] { "Key1" , "Key2" , "Key3" } , options . InstantiatedIDictionary . Keys ) ;
1804+ Assert . Equal ( new string [ ] { "value1" , "value2" , "value3" } , options . InstantiatedIDictionary . Values ) ;
1805+ Assert . True ( options . IsSameInstantiatedIDictionary ( ) ) ;
1806+
1807+ Assert . True ( 3 == options . InstantiatedIReadOnlyDictionary . Count ( ) , $ "InstantiatedIReadOnlyDictionary count is { options . InstantiatedIReadOnlyDictionary . Count ( ) } .. { options . InstantiatedIReadOnlyDictionary . ElementAt ( options . InstantiatedIReadOnlyDictionary . Count ( ) - 1 ) } ") ;
1808+ Assert . Equal ( new string [ ] { "Key1" , "Key2" , "Key3" } , options . InstantiatedIReadOnlyDictionary . Keys ) ;
1809+ Assert . Equal ( new string [ ] { "value1" , "value2" , "value3" } , options . InstantiatedIReadOnlyDictionary . Values ) ;
1810+ Assert . False ( options . IsSameInstantiatedIReadOnlyDictionary ( ) ) ;
1811+
1812+ Assert . Equal ( 1 , options . UnInstantiatedIReadOnlyDictionary . Count ( ) ) ;
1813+ Assert . Equal ( new string [ ] { "Key" } , options . UnInstantiatedIReadOnlyDictionary . Keys ) ;
1814+ Assert . Equal ( new string [ ] { "value" } , options . UnInstantiatedIReadOnlyDictionary . Values ) ;
1815+
1816+ Assert . True ( 3 == options . InstantiatedISet . Count ( ) , $ "InstantiatedISet count is { options . InstantiatedISet . Count ( ) } .. { string . Join ( ", " , options . InstantiatedISet ) } .. { options . IsSameInstantiatedISet ( ) } ") ;
1817+ Assert . Equal ( new string [ ] { "a" , "b" , "C" } , options . InstantiatedISet ) ;
1818+ Assert . True ( options . IsSameInstantiatedISet ( ) ) ;
1819+
1820+ Assert . True ( 3 == options . UnInstantiatedISet . Count ( ) , $ "UnInstantiatedISet count is { options . UnInstantiatedISet . Count ( ) } .. { options . UnInstantiatedISet . ElementAt ( options . UnInstantiatedISet . Count ( ) - 1 ) } ") ;
1821+ Assert . Equal ( new string [ ] { "a" , "A" , "B" } , options . UnInstantiatedISet ) ;
1822+
1823+ #if NETCOREAPP
1824+ Assert . True ( 3 == options . InstantiatedIReadOnlySet . Count ( ) , $ "InstantiatedIReadOnlySet count is { options . InstantiatedIReadOnlySet . Count ( ) } .. { options . InstantiatedIReadOnlySet . ElementAt ( options . InstantiatedIReadOnlySet . Count ( ) - 1 ) } ") ;
1825+ Assert . Equal ( new string [ ] { "a" , "b" , "Z" } , options . InstantiatedIReadOnlySet ) ;
1826+ Assert . False ( options . IsSameInstantiatedIReadOnlySet ( ) ) ;
1827+
1828+ Assert . Equal ( 2 , options . UnInstantiatedIReadOnlySet . Count ( ) ) ;
1829+ Assert . Equal ( new string [ ] { "y" , "z" } , options . UnInstantiatedIReadOnlySet ) ;
1830+ #endif
1831+ Assert . Equal ( 4 , options . InstantiatedICollection . Count ( ) ) ;
1832+ Assert . Equal ( new string [ ] { "a" , "b" , "c" , "d" } , options . InstantiatedICollection ) ;
1833+ Assert . True ( options . IsSameInstantiatedICollection ( ) ) ;
1834+
1835+ Assert . Equal ( 2 , options . UnInstantiatedICollection . Count ( ) ) ;
1836+ Assert . Equal ( new string [ ] { "t" , "a" } , options . UnInstantiatedICollection ) ;
1837+
1838+ Assert . Equal ( 4 , options . InstantiatedIReadOnlyCollection . Count ( ) ) ;
1839+ Assert . Equal ( new string [ ] { "a" , "b" , "c" , "d" } , options . InstantiatedIReadOnlyCollection ) ;
1840+ Assert . False ( options . IsSameInstantiatedIReadOnlyCollection ( ) ) ;
1841+
1842+ Assert . Equal ( 2 , options . UnInstantiatedIReadOnlyCollection . Count ( ) ) ;
1843+ Assert . Equal ( new string [ ] { "r" , "e" } , options . UnInstantiatedIReadOnlyCollection ) ;
1844+ }
1845+
1846+ [ Fact ]
1847+ public void TestMutatingDictionaryValues ( )
1848+ {
1849+ IConfiguration config = new ConfigurationBuilder ( )
1850+ . AddInMemoryCollection ( )
1851+ . Build ( ) ;
1852+
1853+ config [ "Key:0" ] = "NewValue" ;
1854+ var dict = new Dictionary < string , string [ ] > ( ) { { "Key" , new [ ] { "InitialValue" } } } ;
1855+
1856+ Assert . Equal ( 1 , dict [ "Key" ] . Length ) ;
1857+ Assert . Equal ( "InitialValue" , dict [ "Key" ] [ 0 ] ) ;
1858+
1859+ // Binding will accumulate to the values inside the dictionary.
1860+ config . Bind ( dict ) ;
1861+ Assert . Equal ( 2 , dict [ "Key" ] . Length ) ;
1862+ Assert . Equal ( "InitialValue" , dict [ "Key" ] [ 0 ] ) ;
1863+ Assert . Equal ( "NewValue" , dict [ "Key" ] [ 1 ] ) ;
1864+ }
16881865 }
16891866}
0 commit comments