Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
dotNET Platform
runtime
提交
c9996085
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,发现更多精彩内容 >>
未验证
提交
c9996085
编写于
8月 09, 2023
作者:
S
Steve Harter
提交者:
GitHub
8月 09, 2023
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Add extension methods for creating OptionsBuilder with ValidateOnStart support (#89973)
上级
ad5ba123
变更
4
隐藏空白更改
内联
并排
Showing
4 changed file
with
167 addition
and
4 deletion
+167
-4
src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/OptionsBuilderExtensionsTests.cs
....Hosting/tests/UnitTests/OptionsBuilderExtensionsTests.cs
+81
-1
src/libraries/Microsoft.Extensions.Options/ref/Microsoft.Extensions.Options.cs
...ft.Extensions.Options/ref/Microsoft.Extensions.Options.cs
+4
-2
src/libraries/Microsoft.Extensions.Options/src/OptionsServiceCollectionExtensions.cs
...ensions.Options/src/OptionsServiceCollectionExtensions.cs
+41
-0
src/libraries/Microsoft.Extensions.Options/tests/Microsoft.Extensions.Options.Tests/OptionsBuilderTest.cs
.../Microsoft.Extensions.Options.Tests/OptionsBuilderTest.cs
+41
-1
未找到文件。
src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/OptionsBuilderExtensionsTests.cs
浏览文件 @
c9996085
...
...
@@ -5,7 +5,6 @@
using
System.Linq
;
using
System.Threading.Tasks
;
using
Microsoft.Extensions.DependencyInjection
;
using
Microsoft.Extensions.Hosting
;
using
Microsoft.Extensions.Options
;
using
Xunit
;
...
...
@@ -124,6 +123,32 @@ public async Task ValidateOnStart_NamedOptions_ValidatesFailureOnStart()
}
}
[
Fact
]
public
async
Task
ValidateOnStart_NamedOptions_ValidatesFailureOnStart_AddOptionsWithValidateOnStart
()
{
var
hostBuilder
=
CreateHostBuilder
(
services
=>
{
services
.
AddOptions
().
AddSingleton
(
new
FakeService
());
services
.
AddOptionsWithValidateOnStart
<
FakeSettings
>(
"named"
)
.
Configure
<
FakeService
>((
o
,
_
)
=>
{
o
.
Name
=
"named"
;
})
.
Validate
(
o
=>
o
.
Name
==
null
,
"trigger validation failure for named option!"
);
});
using
(
var
host
=
hostBuilder
.
Build
())
{
var
error
=
await
Assert
.
ThrowsAsync
<
OptionsValidationException
>(
async
()
=>
{
await
host
.
StartAsync
();
});
ValidateFailure
<
FakeSettings
>(
error
,
1
,
"trigger validation failure for named option!"
);
}
}
[
Fact
]
private
async
Task
ValidateOnStart_AddNamedOptionsMultipleTimesForSameType_BothGetTriggered
()
{
...
...
@@ -195,6 +220,61 @@ private async Task ValidateOnStart_AddEagerValidation_DoesValidationWhenHostStar
Assert
.
True
(
validateCalled
);
}
[
Fact
]
private
async
Task
ValidateOnStart_AddEagerValidation_DoesValidationWhenHostStartsWithNoFailure_AddOptionsWithValidateOnStart
()
{
bool
validateCalled
=
false
;
var
hostBuilder
=
CreateHostBuilder
(
services
=>
{
// Adds eager validation using ValidateOnStart
services
.
AddOptionsWithValidateOnStart
<
ComplexOptions
>(
"correct_configuration"
)
.
Configure
(
o
=>
o
.
Boolean
=
true
)
.
Validate
(
o
=>
{
validateCalled
=
true
;
return
o
.
Boolean
;
},
"correct_configuration"
);
});
using
(
var
host
=
hostBuilder
.
Build
())
{
await
host
.
StartAsync
();
}
Assert
.
True
(
validateCalled
);
}
[
Fact
]
private
async
void
CanValidateOptionsEagerly_AddOptionsWithValidateOnStart_IValidateOptions
()
{
var
hostBuilder
=
CreateHostBuilder
(
services
=>
services
.
AddOptionsWithValidateOnStart
<
ComplexOptions
,
ComplexOptionsValidator
>()
.
Configure
(
o
=>
o
.
Boolean
=
false
));
using
(
var
host
=
hostBuilder
.
Build
())
{
var
error
=
await
Assert
.
ThrowsAsync
<
OptionsValidationException
>(
async
()
=>
{
await
host
.
StartAsync
();
});
ValidateFailure
<
ComplexOptions
>(
error
,
1
,
"Boolean != true"
);
}
}
private
class
ComplexOptionsValidator
:
IValidateOptions
<
ComplexOptions
>
{
public
ValidateOptionsResult
Validate
(
string
name
,
ComplexOptions
options
)
{
if
(
options
.
Boolean
==
true
)
{
return
ValidateOptionsResult
.
Success
;
}
return
ValidateOptionsResult
.
Fail
(
"Boolean != true"
);
}
}
[
Fact
]
private
async
Task
ValidateOnStart_AddLazyValidation_SkipsValidationWhenHostStarts
()
{
...
...
src/libraries/Microsoft.Extensions.Options/ref/Microsoft.Extensions.Options.cs
浏览文件 @
c9996085
...
...
@@ -13,6 +13,8 @@ public static partial class OptionsBuilderExtensions
public
static
partial
class
OptionsServiceCollectionExtensions
{
public
static
Microsoft
.
Extensions
.
DependencyInjection
.
IServiceCollection
AddOptions
(
this
Microsoft
.
Extensions
.
DependencyInjection
.
IServiceCollection
services
)
{
throw
null
;
}
public
static
Microsoft
.
Extensions
.
Options
.
OptionsBuilder
<
TOptions
>
AddOptionsWithValidateOnStart
<[
System
.
Diagnostics
.
CodeAnalysis
.
DynamicallyAccessedMembers
(
System
.
Diagnostics
.
CodeAnalysis
.
DynamicallyAccessedMemberTypes
.
All
)]
TOptions
>(
this
Microsoft
.
Extensions
.
DependencyInjection
.
IServiceCollection
services
,
string
?
name
=
null
)
where
TOptions
:
class
{
throw
null
;
}
public
static
Microsoft
.
Extensions
.
Options
.
OptionsBuilder
<
TOptions
>
AddOptionsWithValidateOnStart
<[
System
.
Diagnostics
.
CodeAnalysis
.
DynamicallyAccessedMembers
(
System
.
Diagnostics
.
CodeAnalysis
.
DynamicallyAccessedMemberTypes
.
All
)]
TOptions
,
[
System
.
Diagnostics
.
CodeAnalysis
.
DynamicallyAccessedMembers
(
System
.
Diagnostics
.
CodeAnalysis
.
DynamicallyAccessedMemberTypes
.
PublicConstructors
)]
TValidateOptions
>(
this
Microsoft
.
Extensions
.
DependencyInjection
.
IServiceCollection
services
,
string
?
name
=
null
)
where
TOptions
:
class
where
TValidateOptions
:
class
,
Microsoft
.
Extensions
.
Options
.
IValidateOptions
<
TOptions
>
{
throw
null
;
}
public
static
Microsoft
.
Extensions
.
Options
.
OptionsBuilder
<
TOptions
>
AddOptions
<
TOptions
>(
this
Microsoft
.
Extensions
.
DependencyInjection
.
IServiceCollection
services
)
where
TOptions
:
class
{
throw
null
;
}
public
static
Microsoft
.
Extensions
.
Options
.
OptionsBuilder
<
TOptions
>
AddOptions
<
TOptions
>(
this
Microsoft
.
Extensions
.
DependencyInjection
.
IServiceCollection
services
,
string
?
name
)
where
TOptions
:
class
{
throw
null
;
}
public
static
Microsoft
.
Extensions
.
DependencyInjection
.
IServiceCollection
ConfigureAll
<
TOptions
>(
this
Microsoft
.
Extensions
.
DependencyInjection
.
IServiceCollection
services
,
System
.
Action
<
TOptions
>
configureOptions
)
where
TOptions
:
class
{
throw
null
;
}
...
...
@@ -141,7 +143,7 @@ public partial interface IPostConfigureOptions<in TOptions> where TOptions : cla
}
public
partial
interface
IStartupValidator
{
public
void
Validate
();
void
Validate
();
}
public
partial
interface
IValidateOptions
<
TOptions
>
where
TOptions
:
class
{
...
...
@@ -323,7 +325,7 @@ public partial class ValidateOptionsResult
public
static
Microsoft
.
Extensions
.
Options
.
ValidateOptionsResult
Fail
(
System
.
Collections
.
Generic
.
IEnumerable
<
string
>
failures
)
{
throw
null
;
}
public
static
Microsoft
.
Extensions
.
Options
.
ValidateOptionsResult
Fail
(
string
failureMessage
)
{
throw
null
;
}
}
public
class
ValidateOptionsResultBuilder
public
partial
class
ValidateOptionsResultBuilder
{
public
ValidateOptionsResultBuilder
()
{
}
public
void
AddError
(
string
error
,
string
?
propertyName
=
null
)
{
}
...
...
src/libraries/Microsoft.Extensions.Options/src/OptionsServiceCollectionExtensions.cs
浏览文件 @
c9996085
...
...
@@ -31,6 +31,47 @@ public static IServiceCollection AddOptions(this IServiceCollection services)
return
services
;
}
/// <summary>
/// Adds services required for using options and enforces options validation check on start rather than in runtime.
/// </summary>
/// <remarks>
/// The <seealso cref="OptionsBuilderExtensions.ValidateOnStart{TOptions}(OptionsBuilder{TOptions})"/> extension is called by this method.
/// </remarks>
/// <typeparam name="TOptions">The options type to be configured.</typeparam>
/// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
/// <param name="name">The name of the options instance.</param>
/// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
public
static
OptionsBuilder
<
TOptions
>
AddOptionsWithValidateOnStart
<
[
DynamicallyAccessedMembers
(
DynamicallyAccessedMemberTypes
.
All
)]
TOptions
>(
this
IServiceCollection
services
,
string
?
name
=
null
)
where
TOptions
:
class
{
return
new
OptionsBuilder
<
TOptions
>(
services
,
name
??
Options
.
Options
.
DefaultName
).
ValidateOnStart
();
}
/// <summary>
/// Adds services required for using options and enforces options validation check on start rather than in runtime.
/// </summary>
/// <remarks>
/// The <seealso cref="OptionsBuilderExtensions.ValidateOnStart{TOptions}(OptionsBuilder{TOptions})"/> extension is called by this method.
/// </remarks>
/// <typeparam name="TOptions">The options type to be configured.</typeparam>
/// <typeparam name="TValidateOptions">The <see cref="IValidateOptions{TOptions}"/> validator type.</typeparam>
/// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
/// <param name="name">The name of the options instance.</param>
/// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
public
static
OptionsBuilder
<
TOptions
>
AddOptionsWithValidateOnStart
<
[
DynamicallyAccessedMembers
(
DynamicallyAccessedMemberTypes
.
All
)]
TOptions
,
[
DynamicallyAccessedMembers
(
DynamicallyAccessedMemberTypes
.
PublicConstructors
)]
TValidateOptions
>(
this
IServiceCollection
services
,
string
?
name
=
null
)
where
TOptions
:
class
where
TValidateOptions
:
class
,
IValidateOptions
<
TOptions
>
{
services
.
AddOptions
().
TryAddEnumerable
(
ServiceDescriptor
.
Singleton
<
IValidateOptions
<
TOptions
>,
TValidateOptions
>());
return
new
OptionsBuilder
<
TOptions
>(
services
,
name
??
Options
.
Options
.
DefaultName
).
ValidateOnStart
();
}
/// <summary>
/// Registers an action used to configure a particular type of options.
/// Note: These are run before all <seealso cref="PostConfigure{TOptions}(IServiceCollection, Action{TOptions})"/>.
...
...
src/libraries/Microsoft.Extensions.Options/tests/Microsoft.Extensions.Options.Tests/OptionsBuilderTest.cs
浏览文件 @
c9996085
...
...
@@ -5,9 +5,9 @@
using
System.Collections.Generic
;
using
System.ComponentModel.DataAnnotations
;
using
System.Linq
;
using
System.Reflection
;
using
Microsoft.Extensions.Configuration
;
using
Microsoft.Extensions.DependencyInjection
;
using
Microsoft.Extensions.DependencyInjection.Extensions
;
using
Xunit
;
namespace
Microsoft.Extensions.Options.Tests
...
...
@@ -321,6 +321,18 @@ public void BadValidatorFailsGracefully()
var
error
=
Assert
.
Throws
<
NotImplementedException
>(()
=>
sp
.
GetRequiredService
<
IOptions
<
FakeOptions
>>().
Value
);
}
private
class
ComplexOptionsValidator
:
IValidateOptions
<
ComplexOptions
>
{
public
ValidateOptionsResult
Validate
(
string
name
,
ComplexOptions
options
)
{
if
(
options
.
Boolean
==
true
)
{
return
ValidateOptionsResult
.
Success
;
}
return
ValidateOptionsResult
.
Fail
(
"Boolean != true"
);
}
}
private
class
MultiOptionValidator
:
IValidateOptions
<
ComplexOptions
>,
IValidateOptions
<
FakeOptions
>
{
private
readonly
string
_allowed
;
...
...
@@ -567,6 +579,34 @@ public void CanValidateOptionsEagerly()
ValidateFailure
<
ComplexOptions
>(
error
,
Options
.
DefaultName
,
3
,
"A validation error has occurred."
,
"Virtual"
,
"Integer"
);
}
[
Fact
]
public
void
CanValidateOptionsEagerly_AddOptionsWithValidateOnStart
()
{
var
services
=
new
ServiceCollection
();
services
.
TryAddEnumerable
(
ServiceDescriptor
.
Singleton
<
IValidateOptions
<
ComplexOptions
>,
ComplexOptionsValidator
>());
services
.
AddOptionsWithValidateOnStart
<
ComplexOptions
>()
.
Configure
(
o
=>
o
.
Boolean
=
false
);
var
sp
=
services
.
BuildServiceProvider
();
// This doesn't really verify eager validation since we have no host to start.
var
error
=
Assert
.
Throws
<
OptionsValidationException
>(()
=>
sp
.
GetRequiredService
<
IOptions
<
ComplexOptions
>>().
Value
);
ValidateFailure
<
ComplexOptions
>(
error
,
Options
.
DefaultName
,
1
,
"Boolean != true"
);
}
[
Fact
]
public
void
CanValidateOptionsEagerly_AddOptionsWithValidateOnStart_IValidateOptions
()
{
var
services
=
new
ServiceCollection
();
services
.
AddOptionsWithValidateOnStart
<
ComplexOptions
,
ComplexOptionsValidator
>()
.
Configure
(
o
=>
o
.
Boolean
=
false
);
var
sp
=
services
.
BuildServiceProvider
();
// This doesn't really verify eager validation since we have no host to start.
var
error
=
Assert
.
Throws
<
OptionsValidationException
>(()
=>
sp
.
GetRequiredService
<
IOptions
<
ComplexOptions
>>().
Value
);
ValidateFailure
<
ComplexOptions
>(
error
,
Options
.
DefaultName
,
1
,
"Boolean != true"
);
}
[
AttributeUsage
(
AttributeTargets
.
Field
|
AttributeTargets
.
Property
,
AllowMultiple
=
false
,
Inherited
=
true
)]
public
class
FromAttribute
:
ValidationAttribute
{
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录