Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
dotNET Platform
runtime
提交
048da75f
R
runtime
项目概览
dotNET Platform
/
runtime
12 个月 前同步成功
通知
1
Star
1
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
R
runtime
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
未验证
提交
048da75f
编写于
3月 17, 2022
作者:
S
Steve Dunn
提交者:
GitHub
3月 17, 2022
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Fixes Binding to non-null IEnumerable doesn't work #36390 (#66131)
上级
c032e0d8
变更
3
隐藏空白更改
内联
并排
Showing
3 changed file
with
299 addition
and
2 deletion
+299
-2
src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs
...xtensions.Configuration.Binder/src/ConfigurationBinder.cs
+48
-2
src/libraries/Microsoft.Extensions.Configuration.Binder/tests/ConfigurationBinderTests.cs
...ns.Configuration.Binder/tests/ConfigurationBinderTests.cs
+107
-0
src/libraries/Microsoft.Extensions.Configuration.Binder/tests/ConfigurationCollectionBindingTests.cs
...ation.Binder/tests/ConfigurationCollectionBindingTests.cs
+144
-0
未找到文件。
src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs
浏览文件 @
048da75f
...
...
@@ -2,8 +2,10 @@
// 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.Diagnostics
;
using
System.Diagnostics.CodeAnalysis
;
using
System.Linq
;
using
System.Reflection
;
...
...
@@ -378,10 +380,19 @@ private static void BindProperty(PropertyInfo property, object instance, IConfig
{
BindCollection
(
instance
,
collectionInterface
,
config
,
options
);
}
// Something else
else
{
BindNonScalar
(
config
,
instance
,
options
);
// See if its an IEnumerable
collectionInterface
=
FindOpenGenericInterface
(
typeof
(
IEnumerable
<>),
type
);
if
(
collectionInterface
!=
null
)
{
instance
=
BindExistingCollection
((
IEnumerable
)
instance
!,
config
,
options
);
}
// Something else
else
{
BindNonScalar
(
config
,
instance
,
options
);
}
}
}
}
...
...
@@ -498,6 +509,41 @@ private static void BindProperty(PropertyInfo property, object instance, IConfig
}
}
[
RequiresUnreferencedCode
(
"Cannot statically analyze what the element type is of the object collection so its members may be trimmed."
)]
private
static
IEnumerable
BindExistingCollection
(
IEnumerable
source
,
IConfiguration
config
,
BinderOptions
options
)
{
// find the interface that is IEnumerable<T>
Type
type
=
source
.
GetType
().
GetInterface
(
"IEnumerable`1"
,
false
)!;
Type
elementType
=
type
.
GenericTypeArguments
[
0
];
Type
genericType
=
typeof
(
List
<>).
MakeGenericType
(
elementType
);
IList
newList
=
(
IList
)
Activator
.
CreateInstance
(
genericType
,
source
)!;
IConfigurationSection
[]
children
=
config
.
GetChildren
().
ToArray
();
for
(
int
i
=
0
;
i
<
children
.
Length
;
i
++)
{
try
{
object
?
item
=
BindInstance
(
type
:
elementType
,
instance
:
null
,
config
:
children
[
i
],
options
:
options
);
if
(
item
!=
null
)
{
newList
.
Add
(
item
);
}
}
catch
{
}
}
return
newList
;
}
[
RequiresUnreferencedCode
(
"Cannot statically analyze what the element type is of the Array so its members may be trimmed."
)]
private
static
Array
BindArray
(
Array
source
,
IConfiguration
config
,
BinderOptions
options
)
{
...
...
src/libraries/Microsoft.Extensions.Configuration.Binder/tests/ConfigurationBinderTests.cs
浏览文件 @
048da75f
...
...
@@ -4,6 +4,7 @@
using
System
;
using
System.Collections.Generic
;
using
System.ComponentModel
;
using
System.Linq
;
using
System.Reflection
;
using
Xunit
;
...
...
@@ -47,6 +48,11 @@ public string ReadOnly
{
get
{
return
null
;
}
}
public
IEnumerable
<
string
>
NonInstantiatedIEnumerable
{
get
;
set
;
}
=
null
!;
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
>();
}
public
class
NestedOptions
...
...
@@ -220,6 +226,107 @@ public void CanBindConfigurationKeyNameAttributes()
Assert
.
Equal
(
"Yo"
,
options
.
NamedProperty
);
}
[
Fact
]
public
void
CanBindNonInstantiatedIEnumerableWithItems
()
{
var
dic
=
new
Dictionary
<
string
,
string
>
{
{
"NonInstantiatedIEnumerable:0"
,
"Yo1"
},
{
"NonInstantiatedIEnumerable:1"
,
"Yo2"
},
};
var
configurationBuilder
=
new
ConfigurationBuilder
();
configurationBuilder
.
AddInMemoryCollection
(
dic
);
var
config
=
configurationBuilder
.
Build
();
var
options
=
config
.
Get
<
ComplexOptions
>()!;
Assert
.
Equal
(
2
,
options
.
NonInstantiatedIEnumerable
.
Count
());
Assert
.
Equal
(
"Yo1"
,
options
.
NonInstantiatedIEnumerable
.
ElementAt
(
0
));
Assert
.
Equal
(
"Yo2"
,
options
.
NonInstantiatedIEnumerable
.
ElementAt
(
1
));
}
[
Fact
]
public
void
CanBindInstantiatedIEnumerableWithItems
()
{
var
dic
=
new
Dictionary
<
string
,
string
>
{
{
"InstantiatedIEnumerable:0"
,
"Yo1"
},
{
"InstantiatedIEnumerable:1"
,
"Yo2"
},
};
var
configurationBuilder
=
new
ConfigurationBuilder
();
configurationBuilder
.
AddInMemoryCollection
(
dic
);
var
config
=
configurationBuilder
.
Build
();
var
options
=
config
.
Get
<
ComplexOptions
>()!;
Assert
.
Equal
(
2
,
options
.
InstantiatedIEnumerable
.
Count
());
Assert
.
Equal
(
"Yo1"
,
options
.
InstantiatedIEnumerable
.
ElementAt
(
0
));
Assert
.
Equal
(
"Yo2"
,
options
.
InstantiatedIEnumerable
.
ElementAt
(
1
));
}
[
Fact
]
public
void
CanBindInstantiatedICollectionWithItems
()
{
var
dic
=
new
Dictionary
<
string
,
string
>
{
{
"InstantiatedICollection:0"
,
"Yo1"
},
{
"InstantiatedICollection:1"
,
"Yo2"
},
};
var
configurationBuilder
=
new
ConfigurationBuilder
();
configurationBuilder
.
AddInMemoryCollection
(
dic
);
var
config
=
configurationBuilder
.
Build
();
var
options
=
config
.
Get
<
ComplexOptions
>()!;
Assert
.
Equal
(
2
,
options
.
InstantiatedICollection
.
Count
());
Assert
.
Equal
(
"Yo1"
,
options
.
InstantiatedICollection
.
ElementAt
(
0
));
Assert
.
Equal
(
"Yo2"
,
options
.
InstantiatedICollection
.
ElementAt
(
1
));
}
[
Fact
]
public
void
CanBindInstantiatedIReadOnlyCollectionWithItems
()
{
var
dic
=
new
Dictionary
<
string
,
string
>
{
{
"InstantiatedIReadOnlyCollection:0"
,
"Yo1"
},
{
"InstantiatedIReadOnlyCollection:1"
,
"Yo2"
},
};
var
configurationBuilder
=
new
ConfigurationBuilder
();
configurationBuilder
.
AddInMemoryCollection
(
dic
);
var
config
=
configurationBuilder
.
Build
();
var
options
=
config
.
Get
<
ComplexOptions
>()!;
Assert
.
Equal
(
2
,
options
.
InstantiatedIReadOnlyCollection
.
Count
);
Assert
.
Equal
(
"Yo1"
,
options
.
InstantiatedIReadOnlyCollection
.
ElementAt
(
0
));
Assert
.
Equal
(
"Yo2"
,
options
.
InstantiatedIReadOnlyCollection
.
ElementAt
(
1
));
}
[
Fact
]
public
void
CanBindInstantiatedIEnumerableWithNullItems
()
{
var
dic
=
new
Dictionary
<
string
,
string
>
{
{
"InstantiatedIEnumerable:0"
,
null
},
{
"InstantiatedIEnumerable:1"
,
"Yo1"
},
{
"InstantiatedIEnumerable:2"
,
"Yo2"
},
};
var
configurationBuilder
=
new
ConfigurationBuilder
();
configurationBuilder
.
AddInMemoryCollection
(
dic
);
var
config
=
configurationBuilder
.
Build
();
var
options
=
config
.
Get
<
ComplexOptions
>()!;
Assert
.
Equal
(
2
,
options
.
InstantiatedIEnumerable
.
Count
());
Assert
.
Equal
(
"Yo1"
,
options
.
InstantiatedIEnumerable
.
ElementAt
(
0
));
Assert
.
Equal
(
"Yo2"
,
options
.
InstantiatedIEnumerable
.
ElementAt
(
1
));
}
[
Fact
]
public
void
EmptyStringIsNullable
()
{
...
...
src/libraries/Microsoft.Extensions.Configuration.Binder/tests/ConfigurationCollectionBindingTests.cs
浏览文件 @
048da75f
...
...
@@ -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.Linq
;
using
Xunit
;
...
...
@@ -958,6 +959,101 @@ public void CanBindUninitializedIEnumerable()
Assert
.
Equal
(
"valx"
,
array
[
3
]);
}
[
Fact
]
public
void
CanBindInitializedIEnumerableAndTheOriginalItemsAreNotMutated
()
{
var
input
=
new
Dictionary
<
string
,
string
>
{
{
"AlreadyInitializedIEnumerableInterface:0"
,
"val0"
},
{
"AlreadyInitializedIEnumerableInterface:1"
,
"val1"
},
{
"AlreadyInitializedIEnumerableInterface:2"
,
"val2"
},
{
"AlreadyInitializedIEnumerableInterface:x"
,
"valx"
}
};
var
configurationBuilder
=
new
ConfigurationBuilder
();
configurationBuilder
.
AddInMemoryCollection
(
input
);
var
config
=
configurationBuilder
.
Build
();
var
options
=
new
InitializedCollectionsOptions
();
config
.
Bind
(
options
);
var
array
=
options
.
AlreadyInitializedIEnumerableInterface
.
ToArray
();
Assert
.
Equal
(
6
,
array
.
Length
);
Assert
.
Equal
(
"This was here too"
,
array
[
0
]);
Assert
.
Equal
(
"Don't touch me!"
,
array
[
1
]);
Assert
.
Equal
(
"val0"
,
array
[
2
]);
Assert
.
Equal
(
"val1"
,
array
[
3
]);
Assert
.
Equal
(
"val2"
,
array
[
4
]);
Assert
.
Equal
(
"valx"
,
array
[
5
]);
// the original list hasn't been touched
Assert
.
Equal
(
2
,
options
.
ListUsedInIEnumerableFieldAndShouldNotBeTouched
.
Count
);
Assert
.
Equal
(
"This was here too"
,
options
.
ListUsedInIEnumerableFieldAndShouldNotBeTouched
.
ElementAt
(
0
));
Assert
.
Equal
(
"Don't touch me!"
,
options
.
ListUsedInIEnumerableFieldAndShouldNotBeTouched
.
ElementAt
(
1
));
}
[
Fact
]
public
void
CanBindInitializedCustomIEnumerableBasedList
()
{
// A field declared as IEnumerable<T> that is instantiated with a class
// that directly implements IEnumerable<T> is still bound, but with
// a new List<T> with the original values copied over.
var
input
=
new
Dictionary
<
string
,
string
>
{
{
"AlreadyInitializedCustomListDerivedFromIEnumerable:0"
,
"val0"
},
{
"AlreadyInitializedCustomListDerivedFromIEnumerable:1"
,
"val1"
},
};
var
configurationBuilder
=
new
ConfigurationBuilder
();
configurationBuilder
.
AddInMemoryCollection
(
input
);
var
config
=
configurationBuilder
.
Build
();
var
options
=
new
InitializedCollectionsOptions
();
config
.
Bind
(
options
);
var
array
=
options
.
AlreadyInitializedCustomListDerivedFromIEnumerable
.
ToArray
();
Assert
.
Equal
(
4
,
array
.
Length
);
Assert
.
Equal
(
"Item1"
,
array
[
0
]);
Assert
.
Equal
(
"Item2"
,
array
[
1
]);
Assert
.
Equal
(
"val0"
,
array
[
2
]);
Assert
.
Equal
(
"val1"
,
array
[
3
]);
}
[
Fact
]
public
void
CanBindInitializedCustomIndirectlyDerivedIEnumerableList
()
{
// A field declared as IEnumerable<T> that is instantiated with a class
// that indirectly implements IEnumerable<T> is still bound, but with
// a new List<T> with the original values copied over.
var
input
=
new
Dictionary
<
string
,
string
>
{
{
"AlreadyInitializedCustomListIndirectlyDerivedFromIEnumerable:0"
,
"val0"
},
{
"AlreadyInitializedCustomListIndirectlyDerivedFromIEnumerable:1"
,
"val1"
},
};
var
configurationBuilder
=
new
ConfigurationBuilder
();
configurationBuilder
.
AddInMemoryCollection
(
input
);
var
config
=
configurationBuilder
.
Build
();
var
options
=
new
InitializedCollectionsOptions
();
config
.
Bind
(
options
);
var
array
=
options
.
AlreadyInitializedCustomListIndirectlyDerivedFromIEnumerable
.
ToArray
();
Assert
.
Equal
(
4
,
array
.
Length
);
Assert
.
Equal
(
"Item1"
,
array
[
0
]);
Assert
.
Equal
(
"Item2"
,
array
[
1
]);
Assert
.
Equal
(
"val0"
,
array
[
2
]);
Assert
.
Equal
(
"val1"
,
array
[
3
]);
}
[
Fact
]
public
void
CanBindUninitializedICollection
()
{
...
...
@@ -1101,6 +1197,28 @@ private class UnintializedCollectionsOptions
public
IReadOnlyDictionary
<
string
,
string
>
IReadOnlyDictionary
{
get
;
set
;
}
}
private
class
InitializedCollectionsOptions
{
public
InitializedCollectionsOptions
()
{
AlreadyInitializedIEnumerableInterface
=
ListUsedInIEnumerableFieldAndShouldNotBeTouched
;
}
public
List
<
string
>
ListUsedInIEnumerableFieldAndShouldNotBeTouched
=
new
List
<
string
>
{
"This was here too"
,
"Don't touch me!"
};
public
IEnumerable
<
string
>
AlreadyInitializedIEnumerableInterface
{
get
;
set
;
}
public
IEnumerable
<
string
>
AlreadyInitializedCustomListDerivedFromIEnumerable
{
get
;
set
;
}
=
new
CustomListDerivedFromIEnumerable
();
public
IEnumerable
<
string
>
AlreadyInitializedCustomListIndirectlyDerivedFromIEnumerable
{
get
;
set
;
}
=
new
CustomListIndirectlyDerivedFromIEnumerable
();
}
private
class
CustomList
:
List
<
string
>
{
// Add an overload, just to make sure binding picks the right Add method
...
...
@@ -1109,6 +1227,32 @@ public void Add(string a, string b)
}
}
private
class
CustomListDerivedFromIEnumerable
:
IEnumerable
<
string
>
{
private
readonly
List
<
string
>
_items
=
new
List
<
string
>
{
"Item1"
,
"Item2"
};
public
IEnumerator
<
string
>
GetEnumerator
()
=>
_items
.
GetEnumerator
();
IEnumerator
IEnumerable
.
GetEnumerator
()
=>
GetEnumerator
();
}
internal
interface
IDerivedOne
:
IDerivedTwo
{
}
internal
interface
IDerivedTwo
:
IEnumerable
<
string
>
{
}
private
class
CustomListIndirectlyDerivedFromIEnumerable
:
IDerivedOne
{
private
readonly
List
<
string
>
_items
=
new
List
<
string
>
{
"Item1"
,
"Item2"
};
public
IEnumerator
<
string
>
GetEnumerator
()
=>
_items
.
GetEnumerator
();
IEnumerator
IEnumerable
.
GetEnumerator
()
=>
GetEnumerator
();
}
private
class
CustomDictionary
<
T
>
:
Dictionary
<
string
,
T
>
{
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录