298 lines
12 KiB
C#
298 lines
12 KiB
C#
// ------------------------------------------------------------------------
|
||
// 版权信息
|
||
// 版权归百小僧及百签科技(广东)有限公司所有。
|
||
// 所有权利保留。
|
||
// 官方网站:https://baiqian.com
|
||
//
|
||
// 许可证信息
|
||
// 项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。
|
||
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
|
||
// ------------------------------------------------------------------------
|
||
|
||
using Microsoft.Extensions.Configuration;
|
||
using Microsoft.Extensions.DependencyInjection;
|
||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||
|
||
using System.Linq.Expressions;
|
||
using System.Reflection;
|
||
|
||
using ThingsGateway.Extension;
|
||
using ThingsGateway.Options;
|
||
|
||
namespace Microsoft.Extensions.Options;
|
||
|
||
/// <summary>
|
||
/// OptionsBuilder 拓展类
|
||
/// </summary>
|
||
[SuppressSniffer]
|
||
public static class OptionsBuilderExtensions
|
||
{
|
||
/// <summary>
|
||
/// 配置选项构建器
|
||
/// </summary>
|
||
/// <typeparam name="TOptions">选项类型</typeparam>
|
||
/// <param name="optionsBuilder">选项构建器实例</param>
|
||
/// <param name="configuration">配置对象</param>
|
||
/// <param name="optionsBuilderType">选项构建器类型,默认为 typeof(TOptions) </param>
|
||
/// <returns>选项构建器实例</returns>
|
||
public static OptionsBuilder<TOptions> ConfigureBuilder<TOptions>(this OptionsBuilder<TOptions> optionsBuilder
|
||
, IConfiguration configuration
|
||
, Type optionsBuilderType = default)
|
||
where TOptions : class
|
||
{
|
||
// 配置默认处理和选项构建器
|
||
optionsBuilder.ConfigureDefaults(configuration)
|
||
.ConfigureBuilder(optionsBuilderType);
|
||
|
||
return optionsBuilder;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 配置多个选项构建器
|
||
/// </summary>
|
||
/// <typeparam name="TOptions">选项类型</typeparam>
|
||
/// <param name="optionsBuilder">选项构建器实例</param>
|
||
/// <param name="configuration">配置对象</param>
|
||
/// <param name="optionsBuilderTypes">配置多个选项构建器</param>
|
||
/// <returns>选项构建器实例</returns>
|
||
/// <exception cref="ArgumentNullException" />
|
||
public static OptionsBuilder<TOptions> ConfigureBuilders<TOptions>(this OptionsBuilder<TOptions> optionsBuilder
|
||
, IConfiguration configuration
|
||
, Type[] optionsBuilderTypes)
|
||
where TOptions : class
|
||
{
|
||
// 配置默认处理和多个选项构建器
|
||
optionsBuilder.ConfigureDefaults(configuration)
|
||
.ConfigureBuilders(optionsBuilderTypes);
|
||
|
||
return optionsBuilder;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 配置选项构建器
|
||
/// </summary>
|
||
/// <typeparam name="TOptions">选项类型</typeparam>
|
||
/// <param name="optionsBuilder">选项构建器实例</param>
|
||
/// <param name="optionsBuilderType">选项构建器类型,默认为 typeof(TOptions) </param>
|
||
/// <returns>选项构建器实例</returns>
|
||
public static OptionsBuilder<TOptions> ConfigureBuilder<TOptions>(this OptionsBuilder<TOptions> optionsBuilder, Type optionsBuilderType = default)
|
||
where TOptions : class
|
||
{
|
||
optionsBuilderType ??= typeof(TOptions);
|
||
var optionsBuilderDependency = typeof(IOptionsBuilderDependency<TOptions>);
|
||
|
||
// 获取所有构建器依赖接口
|
||
var builderInterfaces = optionsBuilderType.GetInterfaces()
|
||
.Where(u => optionsBuilderDependency.IsAssignableFrom(u) && u != optionsBuilderDependency);
|
||
|
||
#pragma warning disable CA1851
|
||
if (!builderInterfaces.Any())
|
||
{
|
||
return optionsBuilder;
|
||
}
|
||
|
||
// 逐条调用 .NET 底层选项配置方法
|
||
foreach (var builderInterface in builderInterfaces)
|
||
{
|
||
InvokeMapMethod(optionsBuilder, optionsBuilderType, builderInterface);
|
||
}
|
||
#pragma warning restore CA1851
|
||
|
||
return optionsBuilder;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 配置多个选项构建器
|
||
/// </summary>
|
||
/// <typeparam name="TOptions">选项类型</typeparam>
|
||
/// <param name="optionsBuilder">选项构建器实例</param>
|
||
/// <param name="optionsBuilderTypes">配置多个选项构建器</param>
|
||
/// <returns>选项构建器实例</returns>
|
||
/// <exception cref="ArgumentNullException" />
|
||
public static OptionsBuilder<TOptions> ConfigureBuilders<TOptions>(this OptionsBuilder<TOptions> optionsBuilder, Type[] optionsBuilderTypes)
|
||
where TOptions : class
|
||
{
|
||
// 处理空对象或空值
|
||
if (optionsBuilderTypes.IsEmpty())
|
||
{
|
||
throw new ArgumentNullException(nameof(optionsBuilderTypes));
|
||
}
|
||
|
||
// 逐条配置选项构建器
|
||
Array.ForEach(optionsBuilderTypes, optionsBuilderType => optionsBuilder.ConfigureBuilder(optionsBuilderType));
|
||
|
||
return optionsBuilder;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 配置选项常规默认处理
|
||
/// </summary>
|
||
/// <typeparam name="TOptions">选项类型</typeparam>
|
||
/// <param name="optionsBuilder">选项构建器实例</param>
|
||
/// <param name="configuration">配置对象</param>
|
||
/// <returns>选项构建器实例</returns>
|
||
public static OptionsBuilder<TOptions> ConfigureDefaults<TOptions>(this OptionsBuilder<TOptions> optionsBuilder, IConfiguration configuration)
|
||
where TOptions : class
|
||
{
|
||
// 获取 [OptionsBuilder] 特性
|
||
var optionsType = typeof(TOptions);
|
||
var optionsBuilderAttribute = typeof(TOptions).GetTypeAttribute<OptionsBuilderAttribute>();
|
||
|
||
// 解析配置类型(自动识别是否是节点配置对象)
|
||
var configurationSection = configuration is IConfigurationSection section
|
||
? section
|
||
: configuration.GetSection(
|
||
string.IsNullOrWhiteSpace(optionsBuilderAttribute?.SectionKey)
|
||
? optionsType.Name.ClearStringAffixes(1, Constants.OptionsTypeSuffix)
|
||
: optionsBuilderAttribute.SectionKey
|
||
);
|
||
|
||
// 绑定配置
|
||
optionsBuilder.Bind(configurationSection, binderOptions =>
|
||
{
|
||
binderOptions.ErrorOnUnknownConfiguration = optionsBuilderAttribute?.ErrorOnUnknownConfiguration ?? false;
|
||
binderOptions.BindNonPublicProperties = optionsBuilderAttribute?.BindNonPublicProperties ?? false;
|
||
});
|
||
|
||
// 注册验证特性支持
|
||
if (optionsBuilderAttribute?.ValidateDataAnnotations == true)
|
||
{
|
||
optionsBuilder.ValidateDataAnnotations()
|
||
.ValidateOnStart();
|
||
}
|
||
|
||
// 注册复杂验证类型
|
||
if (optionsBuilderAttribute?.ValidateOptionsTypes.IsEmpty() == false)
|
||
{
|
||
var validateOptionsType = typeof(IValidateOptions<TOptions>);
|
||
|
||
// 注册复杂选项
|
||
Array.ForEach(optionsBuilderAttribute.ValidateOptionsTypes!, type => optionsBuilder.Services.TryAddEnumerable(ServiceDescriptor.Singleton(validateOptionsType, type)));
|
||
}
|
||
|
||
return optionsBuilder;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 调用 OptionsBuilder{TOptions} 对应方法
|
||
/// </summary>
|
||
/// <param name="optionsBuilder">选项构建器实例</param>
|
||
/// <param name="optionsBuilderType">选项构建器类型</param>
|
||
/// <param name="builderInterface">构建器接口</param>
|
||
private static void InvokeMapMethod(object optionsBuilder
|
||
, Type optionsBuilderType
|
||
, Type builderInterface)
|
||
{
|
||
// 获取接口对应 OptionsBuilder 方法映射特性
|
||
var optionsBuilderMethodMapAttribute = builderInterface.GetCustomAttribute<OptionsBuilderMethodMapAttribute>()!;
|
||
var methodName = optionsBuilderMethodMapAttribute.MethodName;
|
||
|
||
// 获取选项构建器接口实际泛型参数
|
||
var genericArguments = builderInterface.GetGenericArguments();
|
||
|
||
// 获取匹配的配置方法
|
||
var bindingAttr = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly;
|
||
var matchMethod = optionsBuilderType.GetMethods(bindingAttr)
|
||
.First(u => u.Name == methodName || u.Name.EndsWith("." + methodName) && u.GetParameters().Length == genericArguments.Length);
|
||
|
||
// 构建表达式实际传入参数
|
||
var parameterExpressions = BuildExpressionCallParameters(matchMethod
|
||
, !optionsBuilderMethodMapAttribute.VoidReturn
|
||
, genericArguments
|
||
, out var args);
|
||
|
||
// 创建 OptionsBuilder<TOptions> 实例对应调用方法表达式
|
||
var callExpression = Expression.Call(Expression.Constant(optionsBuilder)
|
||
, methodName
|
||
, genericArguments.IsEmpty() ? default : genericArguments!.Skip(1).ToArray()
|
||
, parameterExpressions);
|
||
|
||
// 创建调用委托
|
||
var @delegate = Expression.Lambda(callExpression, parameterExpressions).Compile();
|
||
|
||
// 动态调用
|
||
@delegate.DynamicInvoke(args);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 构建 Call 调用方法表达式参数
|
||
/// </summary>
|
||
/// <remarks>含实际传入参数</remarks>
|
||
/// <param name="matchMethod">表达式匹配方法</param>
|
||
/// <param name="isValidateMethod">是否校验方法</param>
|
||
/// <param name="genericArguments">泛型参数</param>
|
||
/// <param name="args">实际传入参数</param>
|
||
/// <returns>调用参数表达式数组</returns>
|
||
private static ParameterExpression[] BuildExpressionCallParameters(MethodInfo matchMethod
|
||
, bool isValidateMethod
|
||
, Type[] genericArguments
|
||
, out object[] args)
|
||
{
|
||
/*
|
||
* 该方法目的是构建符合 OptionsBuilder 对象的 Configure、PostConfigure、Validate 方法签名委托参数表达式,如:
|
||
* Configure/PostConfigure: [Method](Action<TDep1, .. TDep5>);
|
||
* Validate: [Method](Func<TOptions, TDep1, .. TDep5, bool>, string);
|
||
*/
|
||
|
||
// 创建调用方法第一个委托参数表达式
|
||
var delegateType = CreateDelegate(genericArguments, !isValidateMethod ? default : typeof(bool));
|
||
|
||
var arg0Expression = Expression.Parameter(delegateType, "arg0");
|
||
var arg0 = matchMethod.CreateDelegate(delegateType, default);
|
||
|
||
// 创建调用方法第二个字符串参数表达式(仅限 Validate 方法使用)
|
||
ParameterExpression arg1Expression = default;
|
||
string arg1 = default;
|
||
|
||
if (isValidateMethod)
|
||
{
|
||
// 获取 [FailureMessage] 特性配置
|
||
arg1 = matchMethod.IsDefined(typeof(FailureMessageAttribute))
|
||
? matchMethod.GetCustomAttribute<FailureMessageAttribute>()!.Text
|
||
: default;
|
||
|
||
if (!string.IsNullOrWhiteSpace(arg1))
|
||
{
|
||
arg1Expression = Expression.Parameter(typeof(string), "arg1");
|
||
}
|
||
}
|
||
|
||
// 设置调用方法实际传入参数
|
||
args = arg1Expression == default
|
||
? new object[] { arg0 }
|
||
: new object[] { arg0, arg1! };
|
||
|
||
// 返回调用方法参数定义表达式
|
||
return arg1Expression == default
|
||
? new[] { arg0Expression }
|
||
: new[] { arg0Expression, arg1Expression };
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建委托类型
|
||
/// </summary>
|
||
/// <param name="inputTypes">输入类型</param>
|
||
/// <param name="outputType">输出类型</param>
|
||
/// <returns>Action或Func 委托类型</returns>
|
||
internal static Type CreateDelegate(Type[] inputTypes, Type outputType = default)
|
||
{
|
||
var isFuncDelegate = outputType != default;
|
||
|
||
// 获取基础委托类型,通过是否带返回值判断
|
||
var baseDelegateType = !isFuncDelegate ? typeof(Action) : typeof(Func<>);
|
||
|
||
// 处理无输入参数委托类型
|
||
if (inputTypes.IsEmpty())
|
||
{
|
||
return !isFuncDelegate
|
||
? baseDelegateType
|
||
: baseDelegateType.MakeGenericType(outputType!);
|
||
}
|
||
|
||
// 处理含输入参数委托类型
|
||
return !isFuncDelegate
|
||
? baseDelegateType.Assembly.GetType($"{baseDelegateType.FullName}`{inputTypes!.Length}")!.MakeGenericType(inputTypes)
|
||
: baseDelegateType.Assembly.GetType($"{(baseDelegateType.FullName![0..^2])}`{inputTypes!.Length + 1}")
|
||
!.MakeGenericType(inputTypes.Concat(new[] { outputType! }).ToArray());
|
||
}
|
||
} |