Compare commits

...

3 Commits

Author SHA1 Message Date
MonkSoul
dd599bd1d6 😊 发布 v4.9.7.221 版本,改进新版本数据验证模块 2025-12-06 05:27:21 +08:00
MonkSoul
4fe76e4dac 😊 重构 新数据验证模块依赖注入机制 2025-12-06 05:15:46 +08:00
MonkSoul
ca3d6f6a55 😊 简化 HTTP 远程请求静态类 HttpRemoteClient 自定义配置 2025-12-05 23:55:26 +08:00
70 changed files with 944 additions and 273 deletions

View File

@@ -13,7 +13,8 @@ body:
label:
description: 使 Furion
options:
- 4.9.7.220 ()
- 4.9.7.221 ()
- 4.9.7.220
- 4.9.7.219
- 4.9.7.218
- 4.9.7.217

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<LangVersion>preview</LangVersion>
<TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks>
<Version>4.9.7.220</Version>
<Version>4.9.7.221</Version>
<ImplicitUsings>enable</ImplicitUsings>
<!--<Nullable>enable</Nullable>-->
<Authors>百小僧</Authors>

View File

@@ -92,7 +92,17 @@ public static class HttpRemoteClient
ObjectDisposedException.ThrowIf(_isDisposed, typeof(HttpRemoteClient));
// 更新配置委托
_configure = configure;
_configure = services =>
{
// 调用自定义配置委托
configure(services);
// 检查 HTTP 远程请求服务是否已注册,若未注册则自动完成注册
if (services.All(u => u.ServiceType != typeof(IHttpRemoteService)))
{
services.AddHttpRemote();
}
};
// 重新初始化服务
Reinitialize();

View File

@@ -34,17 +34,14 @@ namespace Furion.Validation;
/// </summary>
/// <typeparam name="T">对象类型</typeparam>
/// <typeparam name="TSelf">派生类型自身类型</typeparam>
public abstract class FluentValidatorBuilder<T, TSelf> where TSelf
: FluentValidatorBuilder<T, TSelf>
public abstract class FluentValidatorBuilder<T, TSelf> : IValidatorInitializer
where TSelf : FluentValidatorBuilder<T, TSelf>
{
/// <summary>
/// 验证上下文数据
/// </summary>
internal readonly IDictionary<object, object?>? _items;
/// <inheritdoc cref="IServiceProvider" />
internal readonly IServiceProvider? _serviceProvider;
/// <summary>
/// 高优先级验证器区域的结束索引(同时也是普通验证器区域的起始索引)
/// </summary>
@@ -60,6 +57,11 @@ public abstract class FluentValidatorBuilder<T, TSelf> where TSelf
/// </summary>
internal ValidatorBase? _lastAddedValidator;
/// <summary>
/// <see cref="IServiceProvider" /> 委托
/// </summary>
internal Func<Type, object?>? _serviceProvider;
/// <summary>
/// <inheritdoc cref="FluentValidatorBuilder{T,TSelf}" />
/// </summary>
@@ -86,7 +88,12 @@ public abstract class FluentValidatorBuilder<T, TSelf> where TSelf
/// <param name="items">验证上下文数据</param>
internal FluentValidatorBuilder(IServiceProvider? serviceProvider, IDictionary<object, object?>? items)
{
_serviceProvider = serviceProvider;
// 空检查
if (serviceProvider is not null)
{
_serviceProvider = serviceProvider.GetService;
}
_items = items;
Validators = [];
}
@@ -101,6 +108,21 @@ public abstract class FluentValidatorBuilder<T, TSelf> where TSelf
/// </summary>
internal List<ValidatorBase> Validators { get; }
/// <inheritdoc />
public virtual void InitializeServiceProvider(Func<Type, object?>? serviceProvider)
{
_serviceProvider = serviceProvider;
// 遍历所有实现 IValidatorInitializer 接口的验证器并同步 IServiceProvider 委托
foreach (var validator in Validators)
{
if (validator is IValidatorInitializer initializer)
{
initializer.InitializeServiceProvider(serviceProvider);
}
}
}
/// <summary>
/// 获取验证器集合
/// </summary>
@@ -575,8 +597,7 @@ public abstract class FluentValidatorBuilder<T, TSelf> where TSelf
// 空检查
ArgumentNullException.ThrowIfNull(condition);
return AddValidator(new MustUnlessValidator<T>(u =>
condition(u, new ValidationContext<T>(u, _serviceProvider, _items?.AsReadOnly()))));
return AddValidator(new MustUnlessValidator<T>(u => condition(u, CreateValidationContext(u))));
}
/// <summary>
@@ -600,8 +621,7 @@ public abstract class FluentValidatorBuilder<T, TSelf> where TSelf
// 空检查
ArgumentNullException.ThrowIfNull(condition);
return AddValidator(new MustValidator<T>(u =>
condition(u, new ValidationContext<T>(u, _serviceProvider, _items?.AsReadOnly()))));
return AddValidator(new MustValidator<T>(u => condition(u, CreateValidationContext(u))));
}
/// <summary>
@@ -683,8 +703,7 @@ public abstract class FluentValidatorBuilder<T, TSelf> where TSelf
// 空检查
ArgumentNullException.ThrowIfNull(condition);
return AddValidator(new PredicateValidator<T>(u =>
condition(u, new ValidationContext<T>(u, _serviceProvider, _items?.AsReadOnly()))));
return AddValidator(new PredicateValidator<T>(u => condition(u, CreateValidationContext(u))));
}
/// <summary>
@@ -898,8 +917,9 @@ public abstract class FluentValidatorBuilder<T, TSelf> where TSelf
/// <returns>
/// <typeparamref name="TSelf" />
/// </returns>
public virtual TSelf AddAnnotations(params ValidationAttribute[] attributes) =>
AddValidator(new ValueAnnotationValidator(attributes, _serviceProvider, _items));
public virtual TSelf AddAnnotations(params ValidationAttribute[] attributes) => AddValidator(
new ValueAnnotationValidator(attributes, null, _items),
validator => validator.InitializeServiceProvider(_serviceProvider));
/// <summary>
/// 构建验证器集合
@@ -908,4 +928,22 @@ public abstract class FluentValidatorBuilder<T, TSelf> where TSelf
/// <see cref="IReadOnlyList{T}" />
/// </returns>
internal IReadOnlyList<ValidatorBase> Build() => Validators;
/// <summary>
/// 创建 <see cref="ValidationContext{T}" /> 实例
/// </summary>
/// <param name="value">对象</param>
/// <returns>
/// <see cref="ValidationContext{T}" />
/// </returns>
internal ValidationContext<T> CreateValidationContext(T value)
{
// 初始化 ValidationContext 实例
var validationContext = new ValidationContext<T>(value, null, _items?.AsReadOnly());
// 同步 IServiceProvider 委托
validationContext.InitializeServiceProvider(_serviceProvider);
return validationContext;
}
}

View File

@@ -58,12 +58,12 @@ public sealed class ValidationBuilder
// 空检查
ArgumentNullException.ThrowIfNull(validatorType);
// 检查类型是否定义了公开无参构造函数
if (!validatorType.HasDefinePublicParameterlessConstructor())
// 检查类型是否可实例化
if (!validatorType.IsInstantiable())
{
throw new ArgumentException(
// ReSharper disable once LocalizableElement
$"Type `{validatorType}` must have a public parameterless constructor to be registered as a validator.",
$"Type `{validatorType}` must be a non-abstract, non-static class to be registered as a validator.",
nameof(validatorType));
}
@@ -120,7 +120,7 @@ public sealed class ValidationBuilder
return AddValidators(assemblies.SelectMany(ass =>
(ass?.GetTypes() ?? Enumerable.Empty<Type>()).Where(t =>
t.HasDefinePublicParameterlessConstructor() && TryGetValidatedType(t, out _))));
t.IsInstantiable() && TryGetValidatedType(t, out _))));
}
/// <summary>
@@ -141,18 +141,44 @@ public sealed class ValidationBuilder
foreach (var (validatorType, modelType) in _validatorTypes)
{
// 注册 IObjectValidator<T> 泛型接口
services.TryAddEnumerable(ServiceDescriptor.Transient(typeof(IObjectValidator<>).MakeGenericType(modelType),
validatorType));
services.Add(ServiceDescriptor.Transient(typeof(IObjectValidator<>).MakeGenericType(modelType),
provider => CreateValidator(provider, validatorType)));
// 注册 AbstractValidator<T> 基类
// services.TryAddEnumerable(
// ServiceDescriptor.Transient(typeof(AbstractValidator<>).MakeGenericType(modelType), validatorType));
// services.Add(ServiceDescriptor.Transient(typeof(AbstractValidator<>).MakeGenericType(modelType),
// provider => CreateValidator(provider, validatorType)));
// 注册 IObjectValidator 非泛型接口
services.TryAddEnumerable(ServiceDescriptor.Transient(typeof(IObjectValidator), validatorType));
services.Add(ServiceDescriptor.Transient(typeof(IObjectValidator),
provider => CreateValidator(provider, validatorType)));
}
}
/// <summary>
/// 创建对象验证器实例
/// </summary>
/// <param name="serviceProvider">
/// <see cref="IServiceProvider" />
/// </param>
/// <param name="validatorType">对象验证器类型</param>
/// <returns>
/// <see cref="IObjectValidator" />
/// </returns>
internal static IObjectValidator CreateValidator(IServiceProvider serviceProvider, Type validatorType)
{
// 空检查
ArgumentNullException.ThrowIfNull(serviceProvider);
ArgumentNullException.ThrowIfNull(validatorType);
// 创建对象验证器实例
var validator = (IObjectValidator)ActivatorUtilities.CreateInstance(serviceProvider, validatorType);
// 同步 IServiceProvider 委托
validator.InitializeServiceProvider(serviceProvider.GetService);
return validator;
}
/// <summary>
/// 检查是否继承自 <see cref="AbstractValidator{T}" /> 抽象基类
/// </summary>

View File

@@ -53,7 +53,7 @@ public static class ValidationExtensions
validationResults?.ToList().ToResults();
/// <summary>
/// 使用对象验证器验证当前实例并返回验证结果集合
/// 创建对象验证器验证当前实例并返回验证结果集合
/// </summary>
/// <param name="validationContext">
/// <see cref="ValidationContext" />
@@ -70,16 +70,44 @@ public static class ValidationExtensions
// 空检查
ArgumentNullException.ThrowIfNull(validationContext);
// 解析 IServiceProvider 服务
var serviceProvider = validationContext.GetService(typeof(IServiceProvider)) as IServiceProvider;
// 初始化 ObjectValidator<T> 实例
// 初始化 ObjectValidator<T> 实例并禁用属性验证特性验证,避免死循环
using var objectValidator = new ObjectValidator<T>(new ValidatorOptions { SuppressAnnotationValidation = true },
serviceProvider, validationContext.Items);
null, validationContext.Items);
// 调用自定义配置委托
configure?.Invoke(objectValidator);
return validationContext.ValidateWith(objectValidator);
}
/// <summary>
/// 使用指定对象验证器验证当前实例并返回验证结果集合
/// </summary>
/// <param name="validationContext">
/// <see cref="ValidationContext" />
/// </param>
/// <param name="objectValidator">
/// <see cref="AbstractValidator{T}" />
/// </param>
/// <typeparam name="T">对象类型</typeparam>
/// <returns>
/// <see cref="IEnumerable{T}" />
/// </returns>
public static IEnumerable<ValidationResult> ValidateWith<T>(this ValidationContext validationContext,
ObjectValidator<T> objectValidator) where T : class
{
// 空检查
ArgumentNullException.ThrowIfNull(validationContext);
// 禁用属性验证特性验证,避免死循环
objectValidator.ConfigureOptions(options =>
{
options.SuppressAnnotationValidation = true;
});
// 同步 IServiceProvider 委托
objectValidator.InitializeServiceProvider(validationContext.GetService);
// 尝试从 Items 中解析规则集列表
string?[]? ruleSets = null;
if (validationContext.Items.TryGetValue(Constants.RULESETS_KEY, out var ruleSetsObj))

View File

@@ -30,7 +30,7 @@ namespace Furion.Validation;
/// <summary>
/// 对象验证器服务
/// </summary>
public interface IObjectValidator;
public interface IObjectValidator : IValidatorInitializer;
/// <summary>
/// <inheritdoc cref="IObjectValidator" />

View File

@@ -49,8 +49,10 @@ public class ObjectValidator<T> : IObjectValidator<T>, IDisposable
/// </summary>
internal readonly Stack<string?> _ruleSetStack;
/// <inheritdoc cref="IServiceProvider" />
internal readonly IServiceProvider? _serviceProvider;
/// <summary>
/// <see cref="IServiceProvider" /> 委托
/// </summary>
internal Func<Type, object?>? _serviceProvider;
/// <summary>
/// <inheritdoc cref="ObjectValidator{T}" />
@@ -89,7 +91,13 @@ public class ObjectValidator<T> : IObjectValidator<T>, IDisposable
ArgumentNullException.ThrowIfNull(options);
Options = options;
_serviceProvider = serviceProvider;
// 空检查
if (serviceProvider is not null)
{
_serviceProvider = serviceProvider.GetService;
}
_items = items;
// 初始化 ObjectAnnotationValidator 实例
@@ -209,6 +217,21 @@ public class ObjectValidator<T> : IObjectValidator<T>, IDisposable
}
}
/// <inheritdoc />
public void InitializeServiceProvider(Func<Type, object?>? serviceProvider)
{
_serviceProvider = serviceProvider;
// 同步 _annotationValidator 实例 IServiceProvider 委托
_annotationValidator.InitializeServiceProvider(serviceProvider);
// 遍历所有属性验证器并同步 IServiceProvider 委托
foreach (var propertyValidator in PropertyValidators)
{
propertyValidator.InitializeServiceProvider(serviceProvider);
}
}
/// <summary>
/// 配置属性验证器
/// </summary>

View File

@@ -245,8 +245,7 @@ public sealed partial class PropertyValidator<T, TProperty> where T : class
var validatorProxy = new ValidatorProxy<T, TValidator>(instance => GetValue(instance),
constructorArgsFactory is null
? null
: instance =>
constructorArgsFactory(new ValidationContext<T>(instance, _serviceProvider, _items?.AsReadOnly())));
: instance => constructorArgsFactory(CreateValidationContext(instance)));
// 空检查
if (configure is not null)

View File

@@ -55,8 +55,7 @@ public sealed partial class
/// <see cref="ObjectValidator{T}" />
/// </param>
internal PropertyValidator(Expression<Func<T, TProperty?>> selector, ObjectValidator<T> objectValidator)
: base((objectValidator ?? throw new ArgumentNullException(nameof(objectValidator)))._serviceProvider,
objectValidator._items)
: base(null, (objectValidator ?? throw new ArgumentNullException(nameof(objectValidator)))._items)
{
// 空检查
ArgumentNullException.ThrowIfNull(selector);
@@ -65,8 +64,10 @@ public sealed partial class
_objectValidator = objectValidator;
// 初始化 PropertyAnnotationValidator 实例
_annotationValidator =
new PropertyAnnotationValidator<T, TProperty>(selector, _serviceProvider, objectValidator._items);
_annotationValidator = new PropertyAnnotationValidator<T, TProperty>(selector, null, objectValidator._items);
// 同步 IServiceProvider 委托
InitializeServiceProvider(objectValidator._serviceProvider);
}
/// <summary>
@@ -206,6 +207,16 @@ public sealed partial class
}
}
/// <inheritdoc />
public override void InitializeServiceProvider(Func<Type, object?>? serviceProvider)
{
// 同步基类 IServiceProvider 委托
base.InitializeServiceProvider(serviceProvider);
// 同步 _annotationValidator 实例 IServiceProvider 委托
_annotationValidator.InitializeServiceProvider(serviceProvider);
}
/// <summary>
/// 设置对象验证器
/// </summary>
@@ -220,6 +231,9 @@ public sealed partial class
{
_propertyValidator = validator;
// 同步 IServiceProvider 委托
_propertyValidator?.InitializeServiceProvider(_serviceProvider);
return this;
}
@@ -438,4 +452,22 @@ public sealed partial class
/// <see cref="string" />
/// </returns>
internal string GetDisplayName() => _annotationValidator.GetDisplayName(DisplayName);
/// <summary>
/// 创建 <see cref="ValidationContext{T}" /> 实例
/// </summary>
/// <param name="value">对象</param>
/// <returns>
/// <see cref="ValidationContext{T}" />
/// </returns>
internal ValidationContext<T> CreateValidationContext(T value)
{
// 初始化 ValidationContext 实例
var validationContext = new ValidationContext<T>(value, null, _items?.AsReadOnly());
// 同步 IServiceProvider 委托
validationContext.InitializeServiceProvider(_serviceProvider);
return validationContext;
}
}

View File

@@ -23,16 +23,19 @@
// 请访问 https://gitee.com/dotnetchina/Furion 获取更多关于 Furion 项目的许可证和版权信息。
// ------------------------------------------------------------------------
using Microsoft.Extensions.DependencyInjection;
namespace Furion.Validation;
/// <summary>
/// 验证上下文
/// </summary>
/// <typeparam name="T">对象类型</typeparam>
public sealed class ValidationContext<T>
public sealed class ValidationContext<T> : IValidatorInitializer
{
/// <summary>
/// <see cref="IServiceProvider" /> 委托
/// </summary>
internal Func<Type, object?>? _serviceProvider;
/// <summary>
/// <inheritdoc cref="ValidationContext{T}" />
/// </summary>
@@ -48,7 +51,13 @@ public sealed class ValidationContext<T>
ArgumentNullException.ThrowIfNull(instance);
Instance = instance;
ServiceProvider = serviceProvider;
// 空检查
if (serviceProvider is not null)
{
_serviceProvider = serviceProvider.GetService;
}
Items = items is not null ? new Dictionary<object, object?>(items) : new Dictionary<object, object?>();
}
@@ -57,14 +66,23 @@ public sealed class ValidationContext<T>
/// </summary>
public T Instance { get; }
/// <inheritdoc cref="IServiceProvider" />
public IServiceProvider? ServiceProvider { get; }
/// <summary>
/// 验证上下文数据
/// </summary>
public IReadOnlyDictionary<object, object?> Items { get; }
/// <inheritdoc />
public void InitializeServiceProvider(Func<Type, object?>? serviceProvider) => _serviceProvider = serviceProvider;
/// <summary>
/// 解析服务
/// </summary>
/// <param name="serviceType">服务类型</param>
/// <returns>
/// <see cref="object" />
/// </returns>
public object? GetService(Type serviceType) => _serviceProvider?.Invoke(serviceType);
/// <summary>
/// 解析服务
/// </summary>
@@ -72,5 +90,5 @@ public sealed class ValidationContext<T>
/// <returns>
/// <typeparamref name="TService" />
/// </returns>
public TService? GetService<TService>() where TService : class => ServiceProvider?.GetService<TService>();
public TService? GetService<TService>() where TService : class => (TService?)GetService(typeof(TService));
}

View File

@@ -30,7 +30,7 @@ namespace Furion.Validation;
/// <summary>
/// 组合验证器
/// </summary>
public class CompositeValidator : ValidatorBase
public class CompositeValidator : ValidatorBase, IValidatorInitializer
{
/// <summary>
/// 高优先级验证器列表
@@ -73,6 +73,19 @@ public class CompositeValidator : ValidatorBase
/// <remarks>默认值为:<see cref="ValidationMode.ValidateAll" />。</remarks>
public ValidationMode Mode { get; set; } = ValidationMode.ValidateAll;
/// <inheritdoc />
public void InitializeServiceProvider(Func<Type, object?>? serviceProvider)
{
// 遍历所有实现 IValidatorInitializer 接口的验证器并同步 IServiceProvider 委托
foreach (var validator in Validators)
{
if (validator is IValidatorInitializer initializer)
{
initializer.InitializeServiceProvider(serviceProvider);
}
}
}
/// <inheritdoc />
public override bool IsValid(object? value)
{

View File

@@ -31,7 +31,7 @@ namespace Furion.Validation;
/// 条件验证器
/// </summary>
/// <typeparam name="T">对象类型</typeparam>
public class ConditionalValidator<T> : ValidatorBase<T>
public class ConditionalValidator<T> : ValidatorBase<T>, IValidatorInitializer
{
/// <summary>
/// 条件和对应的验证器列表
@@ -64,6 +64,22 @@ public class ConditionalValidator<T> : ValidatorBase<T>
ErrorMessageResourceAccessor = () => null!;
}
/// <inheritdoc />
public void InitializeServiceProvider(Func<Type, object?>? serviceProvider)
{
// 组合所有条件的验证器
var validators = (_defaultValidators ?? []).Concat(_conditions.SelectMany(u => u.Validators));
// 遍历所有实现 IValidatorInitializer 接口的验证器并同步 IServiceProvider 委托
foreach (var validator in validators)
{
if (validator is IValidatorInitializer initializer)
{
initializer.InitializeServiceProvider(serviceProvider);
}
}
}
/// <inheritdoc />
public override bool IsValid(T? instance)
{

View File

@@ -0,0 +1,38 @@
// ------------------------------------------------------------------------
// 版权信息
// 版权归百小僧及百签科技(广东)有限公司所有。
// 所有权利保留。
// 官方网站https://baiqian.com
//
// 许可证信息
// Furion 项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
// 官方网站https://furion.net
//
// 使用条款
// 使用本代码应遵守相关法律法规和许可证的要求。
//
// 免责声明
// 对于因使用本代码而产生的任何直接、间接、偶然、特殊或后果性损害,我们不承担任何责任。
//
// 其他重要信息
// Furion 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。
// 有关 Furion 项目的其他详细信息,请参阅位于源代码树根目录中的 COPYRIGHT 和 DISCLAIMER 文件。
//
// 更多信息
// 请访问 https://gitee.com/dotnetchina/Furion 获取更多关于 Furion 项目的许可证和版权信息。
// ------------------------------------------------------------------------
namespace Furion.Validation;
/// <summary>
/// 定义验证器的 <see cref="IServiceProvider" /> 初始化行为
/// </summary>
public interface IValidatorInitializer
{
/// <summary>
/// 初始化(同步) <see cref="IServiceProvider" /> 委托
/// </summary>
/// <param name="serviceProvider"><see cref="IServiceProvider" /> 委托</param>
void InitializeServiceProvider(Func<Type, object?>? serviceProvider);
}

View File

@@ -31,20 +31,25 @@ namespace Furion.Validation;
/// 对象验证特性验证器
/// </summary>
/// <remarks>支持使用 <c>[ValidateNever]</c> 特性来跳过对特定属性的验证,仅限于 ASP.NET Core 应用项目。</remarks>
public class ObjectAnnotationValidator : ValidatorBase
public class ObjectAnnotationValidator : ValidatorBase, IValidatorInitializer
{
/// <summary>
/// 验证上下文数据
/// </summary>
internal readonly IDictionary<object, object?>? _items;
/// <inheritdoc cref="IServiceProvider" />
internal readonly IServiceProvider? _serviceProvider;
/// <summary>
/// <see cref="IServiceProvider" /> 委托
/// </summary>
internal Func<Type, object?>? _serviceProvider;
/// <summary>
/// <inheritdoc cref="ObjectAnnotationValidator" />
/// </summary>
public ObjectAnnotationValidator() => ErrorMessageResourceAccessor = () => null!;
public ObjectAnnotationValidator()
: this(null, null)
{
}
/// <summary>
/// <inheritdoc cref="ObjectAnnotationValidator" />
@@ -63,10 +68,15 @@ public class ObjectAnnotationValidator : ValidatorBase
/// </param>
/// <param name="items">验证上下文数据</param>
public ObjectAnnotationValidator(IServiceProvider? serviceProvider, IDictionary<object, object?>? items)
: this()
{
// 空检查
if (serviceProvider is not null)
{
_serviceProvider = serviceProvider.GetService;
}
_items = items;
_serviceProvider = serviceProvider;
ErrorMessageResourceAccessor = () => null!;
}
/// <summary>
@@ -79,14 +89,16 @@ public class ObjectAnnotationValidator : ValidatorBase
/// </remarks>
public bool ValidateAllProperties { get; set; } = true;
/// <inheritdoc />
public void InitializeServiceProvider(Func<Type, object?>? serviceProvider) => _serviceProvider = serviceProvider;
/// <inheritdoc />
public override bool IsValid(object? value)
{
// 空检查
ArgumentNullException.ThrowIfNull(value);
return Validator.TryValidateObject(value, new ValidationContext(value, _serviceProvider, _items), null,
ValidateAllProperties);
return Validator.TryValidateObject(value, CreateValidationContext(value), null, ValidateAllProperties);
}
/// <inheritdoc />
@@ -105,8 +117,7 @@ public class ObjectAnnotationValidator : ValidatorBase
* 参考源码:
* https://github.com/dotnet/runtime/blob/5535e31a712343a63f5d7d796cd874e563e5ac14/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/Validator.cs#L423-L430
*/
Validator.TryValidateObject(value, new ValidationContext(value, _serviceProvider, _items), validationResults,
ValidateAllProperties);
Validator.TryValidateObject(value, CreateValidationContext(value), validationResults, ValidateAllProperties);
// 如果验证未通过且配置了自定义错误信息,则在首部添加自定义错误信息
if (validationResults.Count > 0 && (string?)ErrorMessageString is not null)
@@ -125,8 +136,7 @@ public class ObjectAnnotationValidator : ValidatorBase
try
{
Validator.ValidateObject(value, new ValidationContext(value, _serviceProvider, _items),
ValidateAllProperties);
Validator.ValidateObject(value, CreateValidationContext(value), ValidateAllProperties);
}
// 如果验证未通过且配置了自定义错误信息,则重新抛出异常
catch (ValidationException e) when (ErrorMessageString is not null)
@@ -142,4 +152,22 @@ public class ObjectAnnotationValidator : ValidatorBase
/// <inheritdoc />
public override string? FormatErrorMessage(string name) =>
(string?)ErrorMessageString is null ? null : base.FormatErrorMessage(name);
/// <summary>
/// 创建 <see cref="ValidationContext" /> 实例
/// </summary>
/// <param name="value">对象</param>
/// <returns>
/// <see cref="ValidationContext" />
/// </returns>
internal ValidationContext CreateValidationContext(object value)
{
// 初始化 ValidationContext 实例
var validationContext = new ValidationContext(value, null, _items);
// 同步 IServiceProvider 委托
validationContext.InitializeServiceProvider(_serviceProvider!);
return validationContext;
}
}

View File

@@ -108,7 +108,7 @@ public class PropertyAnnotationValidator<T, TProperty> : PropertyAnnotationValid
/// 属性验证特性验证器
/// </summary>
/// <typeparam name="T">对象类型</typeparam>
public class PropertyAnnotationValidator<T> : ValidatorBase<T>
public class PropertyAnnotationValidator<T> : ValidatorBase<T>, IValidatorInitializer
where T : class
{
/// <summary>
@@ -121,22 +121,18 @@ public class PropertyAnnotationValidator<T> : ValidatorBase<T>
/// </summary>
internal readonly IDictionary<object, object?>? _items;
/// <inheritdoc cref="IServiceProvider" />
internal readonly IServiceProvider? _serviceProvider;
/// <summary>
/// <see cref="IServiceProvider" /> 委托
/// </summary>
internal Func<Type, object?>? _serviceProvider;
/// <summary>
/// <inheritdoc cref="PropertyAnnotationValidator{T}" />
/// </summary>
/// <param name="selector">属性选择器</param>
public PropertyAnnotationValidator(Expression<Func<T, object?>> selector)
: this(selector, null, null)
{
// 空检查
ArgumentNullException.ThrowIfNull(selector);
Property = selector.GetProperty();
ErrorMessageResourceAccessor = () => null!;
_getter = selector.Compile();
}
/// <summary>
@@ -159,10 +155,21 @@ public class PropertyAnnotationValidator<T> : ValidatorBase<T>
/// <param name="items">验证上下文数据</param>
public PropertyAnnotationValidator(Expression<Func<T, object?>> selector, IServiceProvider? serviceProvider,
IDictionary<object, object?>? items)
: this(selector)
{
// 空检查
ArgumentNullException.ThrowIfNull(selector);
Property = selector.GetProperty();
_getter = selector.Compile();
// 空检查
if (serviceProvider is not null)
{
_serviceProvider = serviceProvider.GetService;
}
_items = items;
_serviceProvider = serviceProvider;
ErrorMessageResourceAccessor = () => null!;
}
/// <summary>
@@ -170,14 +177,17 @@ public class PropertyAnnotationValidator<T> : ValidatorBase<T>
/// </summary>
public PropertyInfo Property { get; }
/// <inheritdoc />
public void InitializeServiceProvider(Func<Type, object?>? serviceProvider) => _serviceProvider = serviceProvider;
/// <inheritdoc />
public override bool IsValid(T? instance)
{
// 空检查
ArgumentNullException.ThrowIfNull(instance);
return Validator.TryValidateProperty(GetValue(instance),
new ValidationContext(instance, _serviceProvider, _items) { MemberName = Property.Name }, null);
return Validator.TryValidateProperty(GetValue(instance), CreateValidationContext(instance, Property.Name),
null);
}
/// <inheritdoc />
@@ -192,8 +202,8 @@ public class PropertyAnnotationValidator<T> : ValidatorBase<T>
// 初始化验证结果集合
var validationResults = new List<ValidationResult>();
Validator.TryValidateProperty(GetValue(instance),
new ValidationContext(instance, _serviceProvider, _items) { MemberName = propertyName }, validationResults);
Validator.TryValidateProperty(GetValue(instance), CreateValidationContext(instance, propertyName),
validationResults);
// 如果验证未通过且配置了自定义错误信息,则在首部添加自定义错误信息
if (validationResults.Count > 0 && (string?)ErrorMessageString is not null)
@@ -216,8 +226,7 @@ public class PropertyAnnotationValidator<T> : ValidatorBase<T>
try
{
Validator.ValidateProperty(GetValue(instance),
new ValidationContext(instance, _serviceProvider, _items) { MemberName = propertyName });
Validator.ValidateProperty(GetValue(instance), CreateValidationContext(instance, propertyName));
}
// 如果验证未通过且配置了自定义错误信息,则重新抛出异常
catch (ValidationException e) when (ErrorMessageString is not null)
@@ -256,4 +265,23 @@ public class PropertyAnnotationValidator<T> : ValidatorBase<T>
public string GetDisplayName(string? name) =>
name ?? Property.GetCustomAttribute<DisplayAttribute>(false)?.Name ??
Property.GetCustomAttribute<DisplayNameAttribute>(false)?.DisplayName ?? Property.Name;
/// <summary>
/// 创建 <see cref="ValidationContext" /> 实例
/// </summary>
/// <param name="value">对象</param>
/// <param name="memberName">成员名称</param>
/// <returns>
/// <see cref="ValidationContext" />
/// </returns>
internal ValidationContext CreateValidationContext(object value, string? memberName)
{
// 初始化 ValidationContext 实例
var validationContext = new ValidationContext(value, null, _items) { MemberName = memberName };
// 同步 IServiceProvider 委托
validationContext.InitializeServiceProvider(_serviceProvider!);
return validationContext;
}
}

View File

@@ -37,7 +37,7 @@ namespace Furion.Validation;
/// <typeparam name="TValidator">
/// <see cref="ValidatorBase" />
/// </typeparam>
public class ValidatorProxy<TValidator> : ValidatorBase, IDisposable
public class ValidatorProxy<TValidator> : ValidatorBase, IDisposable, IValidatorInitializer
where TValidator : ValidatorBase
{
/// <summary>
@@ -65,6 +65,17 @@ public class ValidatorProxy<TValidator> : ValidatorBase, IDisposable
GC.SuppressFinalize(this);
}
/// <inheritdoc />
public void InitializeServiceProvider(Func<Type, object?>? serviceProvider)
{
// 检查验证器是否实现 IValidatorInitializer 接口
if (Validator is IValidatorInitializer initializer)
{
// 同步 IServiceProvider 委托
initializer.InitializeServiceProvider(serviceProvider);
}
}
/// <summary>
/// 配置验证器实例
/// </summary>

View File

@@ -30,15 +30,17 @@ namespace Furion.Validation;
/// <summary>
/// 单个值验证特性验证器
/// </summary>
public class ValueAnnotationValidator : ValidatorBase
public class ValueAnnotationValidator : ValidatorBase, IValidatorInitializer
{
/// <summary>
/// 验证上下文数据
/// </summary>
internal readonly IDictionary<object, object?>? _items;
/// <inheritdoc cref="IServiceProvider" />
internal readonly IServiceProvider? _serviceProvider;
/// <summary>
/// <see cref="IServiceProvider" /> 委托
/// </summary>
internal Func<Type, object?>? _serviceProvider;
/// <summary>
/// <inheritdoc cref="ValueAnnotationValidator" />
@@ -46,19 +48,8 @@ public class ValueAnnotationValidator : ValidatorBase
/// <param name="attributes">验证特性列表</param>
/// <exception cref="ArgumentException"></exception>
public ValueAnnotationValidator(params ValidationAttribute[] attributes)
: this(attributes, null, null)
{
// 空检查
ArgumentNullException.ThrowIfNull(attributes);
// 确保数组元素不存在 null 值
if (attributes.Any(u => (ValidationAttribute?)u is null))
{
// ReSharper disable once LocalizableElement
throw new ArgumentException("Attributes cannot contain null elements.", nameof(attributes));
}
Attributes = attributes;
ErrorMessageResourceAccessor = () => null!;
}
/// <summary>
@@ -81,10 +72,27 @@ public class ValueAnnotationValidator : ValidatorBase
/// <param name="items">验证上下文数据</param>
public ValueAnnotationValidator(ValidationAttribute[] attributes, IServiceProvider? serviceProvider,
IDictionary<object, object?>? items)
: this(attributes)
{
// 空检查
ArgumentNullException.ThrowIfNull(attributes);
// 确保数组元素不存在 null 值
if (attributes.Any(u => (ValidationAttribute?)u is null))
{
// ReSharper disable once LocalizableElement
throw new ArgumentException("Attributes cannot contain null elements.", nameof(attributes));
}
Attributes = attributes;
// 空检查
if (serviceProvider is not null)
{
_serviceProvider = serviceProvider.GetService;
}
_items = items;
_serviceProvider = serviceProvider;
ErrorMessageResourceAccessor = () => null!;
}
/// <summary>
@@ -92,10 +100,12 @@ public class ValueAnnotationValidator : ValidatorBase
/// </summary>
public ValidationAttribute[] Attributes { get; }
/// <inheritdoc />
public void InitializeServiceProvider(Func<Type, object?>? serviceProvider) => _serviceProvider = serviceProvider;
/// <inheritdoc />
public override bool IsValid(object? value) =>
Validator.TryValidateValue(value, new ValidationContext(new object(), _serviceProvider, _items), null,
Attributes);
Validator.TryValidateValue(value, CreateValidationContext(new object(), null), null, Attributes);
/// <inheritdoc />
public override List<ValidationResult>? GetValidationResults(object? value, string name)
@@ -103,9 +113,8 @@ public class ValueAnnotationValidator : ValidatorBase
// 初始化属性名称和验证结果集合
var (memberName, validationResults) = (GetMemberName(name), new List<ValidationResult>());
Validator.TryValidateValue(value,
new ValidationContext(new object(), _serviceProvider, _items) { MemberName = memberName },
validationResults, Attributes);
Validator.TryValidateValue(value, CreateValidationContext(new object(), memberName), validationResults,
Attributes);
// 如果验证未通过且配置了自定义错误信息,则在首部添加自定义错误信息
if (validationResults.Count > 0 && (string?)ErrorMessageString is not null)
@@ -124,8 +133,7 @@ public class ValueAnnotationValidator : ValidatorBase
try
{
Validator.ValidateValue(value,
new ValidationContext(new object(), _serviceProvider, _items) { MemberName = memberName }, Attributes);
Validator.ValidateValue(value, CreateValidationContext(new object(), memberName), Attributes);
}
// 如果验证未通过且配置了自定义错误信息,则重新抛出异常
catch (ValidationException e) when (ErrorMessageString is not null)
@@ -147,4 +155,23 @@ public class ValueAnnotationValidator : ValidatorBase
/// <see cref="string" />
/// </returns>
internal static string GetMemberName(string name) => string.IsNullOrEmpty(name) ? "Value" : name;
/// <summary>
/// 创建 <see cref="ValidationContext" /> 实例
/// </summary>
/// <param name="value">对象</param>
/// <param name="memberName">成员名称</param>
/// <returns>
/// <see cref="ValidationContext" />
/// </returns>
internal ValidationContext CreateValidationContext(object value, string? memberName)
{
// 初始化 ValidationContext 实例
var validationContext = new ValidationContext(value, null, _items) { MemberName = memberName };
// 同步 IServiceProvider 委托
validationContext.InitializeServiceProvider(_serviceProvider!);
return validationContext;
}
}

View File

@@ -92,7 +92,17 @@ public static class HttpRemoteClient
ObjectDisposedException.ThrowIf(_isDisposed, typeof(HttpRemoteClient));
// 更新配置委托
_configure = configure;
_configure = services =>
{
// 调用自定义配置委托
configure(services);
// 检查 HTTP 远程请求服务是否已注册,若未注册则自动完成注册
if (services.All(u => u.ServiceType != typeof(IHttpRemoteService)))
{
services.AddHttpRemote();
}
};
// 重新初始化服务
Reinitialize();

View File

@@ -34,17 +34,14 @@ namespace Furion.Validation;
/// </summary>
/// <typeparam name="T">对象类型</typeparam>
/// <typeparam name="TSelf">派生类型自身类型</typeparam>
public abstract class FluentValidatorBuilder<T, TSelf> where TSelf
: FluentValidatorBuilder<T, TSelf>
public abstract class FluentValidatorBuilder<T, TSelf> : IValidatorInitializer
where TSelf : FluentValidatorBuilder<T, TSelf>
{
/// <summary>
/// 验证上下文数据
/// </summary>
internal readonly IDictionary<object, object?>? _items;
/// <inheritdoc cref="IServiceProvider" />
internal readonly IServiceProvider? _serviceProvider;
/// <summary>
/// 高优先级验证器区域的结束索引(同时也是普通验证器区域的起始索引)
/// </summary>
@@ -60,6 +57,11 @@ public abstract class FluentValidatorBuilder<T, TSelf> where TSelf
/// </summary>
internal ValidatorBase? _lastAddedValidator;
/// <summary>
/// <see cref="IServiceProvider" /> 委托
/// </summary>
internal Func<Type, object?>? _serviceProvider;
/// <summary>
/// <inheritdoc cref="FluentValidatorBuilder{T,TSelf}" />
/// </summary>
@@ -86,7 +88,12 @@ public abstract class FluentValidatorBuilder<T, TSelf> where TSelf
/// <param name="items">验证上下文数据</param>
internal FluentValidatorBuilder(IServiceProvider? serviceProvider, IDictionary<object, object?>? items)
{
_serviceProvider = serviceProvider;
// 空检查
if (serviceProvider is not null)
{
_serviceProvider = serviceProvider.GetService;
}
_items = items;
Validators = [];
}
@@ -101,6 +108,21 @@ public abstract class FluentValidatorBuilder<T, TSelf> where TSelf
/// </summary>
internal List<ValidatorBase> Validators { get; }
/// <inheritdoc />
public virtual void InitializeServiceProvider(Func<Type, object?>? serviceProvider)
{
_serviceProvider = serviceProvider;
// 遍历所有实现 IValidatorInitializer 接口的验证器并同步 IServiceProvider 委托
foreach (var validator in Validators)
{
if (validator is IValidatorInitializer initializer)
{
initializer.InitializeServiceProvider(serviceProvider);
}
}
}
/// <summary>
/// 获取验证器集合
/// </summary>
@@ -575,8 +597,7 @@ public abstract class FluentValidatorBuilder<T, TSelf> where TSelf
// 空检查
ArgumentNullException.ThrowIfNull(condition);
return AddValidator(new MustUnlessValidator<T>(u =>
condition(u, new ValidationContext<T>(u, _serviceProvider, _items?.AsReadOnly()))));
return AddValidator(new MustUnlessValidator<T>(u => condition(u, CreateValidationContext(u))));
}
/// <summary>
@@ -600,8 +621,7 @@ public abstract class FluentValidatorBuilder<T, TSelf> where TSelf
// 空检查
ArgumentNullException.ThrowIfNull(condition);
return AddValidator(new MustValidator<T>(u =>
condition(u, new ValidationContext<T>(u, _serviceProvider, _items?.AsReadOnly()))));
return AddValidator(new MustValidator<T>(u => condition(u, CreateValidationContext(u))));
}
/// <summary>
@@ -683,8 +703,7 @@ public abstract class FluentValidatorBuilder<T, TSelf> where TSelf
// 空检查
ArgumentNullException.ThrowIfNull(condition);
return AddValidator(new PredicateValidator<T>(u =>
condition(u, new ValidationContext<T>(u, _serviceProvider, _items?.AsReadOnly()))));
return AddValidator(new PredicateValidator<T>(u => condition(u, CreateValidationContext(u))));
}
/// <summary>
@@ -898,8 +917,9 @@ public abstract class FluentValidatorBuilder<T, TSelf> where TSelf
/// <returns>
/// <typeparamref name="TSelf" />
/// </returns>
public virtual TSelf AddAnnotations(params ValidationAttribute[] attributes) =>
AddValidator(new ValueAnnotationValidator(attributes, _serviceProvider, _items));
public virtual TSelf AddAnnotations(params ValidationAttribute[] attributes) => AddValidator(
new ValueAnnotationValidator(attributes, null, _items),
validator => validator.InitializeServiceProvider(_serviceProvider));
/// <summary>
/// 构建验证器集合
@@ -908,4 +928,22 @@ public abstract class FluentValidatorBuilder<T, TSelf> where TSelf
/// <see cref="IReadOnlyList{T}" />
/// </returns>
internal IReadOnlyList<ValidatorBase> Build() => Validators;
/// <summary>
/// 创建 <see cref="ValidationContext{T}" /> 实例
/// </summary>
/// <param name="value">对象</param>
/// <returns>
/// <see cref="ValidationContext{T}" />
/// </returns>
internal ValidationContext<T> CreateValidationContext(T value)
{
// 初始化 ValidationContext 实例
var validationContext = new ValidationContext<T>(value, null, _items?.AsReadOnly());
// 同步 IServiceProvider 委托
validationContext.InitializeServiceProvider(_serviceProvider);
return validationContext;
}
}

View File

@@ -58,12 +58,12 @@ public sealed class ValidationBuilder
// 空检查
ArgumentNullException.ThrowIfNull(validatorType);
// 检查类型是否定义了公开无参构造函数
if (!validatorType.HasDefinePublicParameterlessConstructor())
// 检查类型是否可实例化
if (!validatorType.IsInstantiable())
{
throw new ArgumentException(
// ReSharper disable once LocalizableElement
$"Type `{validatorType}` must have a public parameterless constructor to be registered as a validator.",
$"Type `{validatorType}` must be a non-abstract, non-static class to be registered as a validator.",
nameof(validatorType));
}
@@ -120,7 +120,7 @@ public sealed class ValidationBuilder
return AddValidators(assemblies.SelectMany(ass =>
(ass?.GetTypes() ?? Enumerable.Empty<Type>()).Where(t =>
t.HasDefinePublicParameterlessConstructor() && TryGetValidatedType(t, out _))));
t.IsInstantiable() && TryGetValidatedType(t, out _))));
}
/// <summary>
@@ -141,18 +141,44 @@ public sealed class ValidationBuilder
foreach (var (validatorType, modelType) in _validatorTypes)
{
// 注册 IObjectValidator<T> 泛型接口
services.TryAddEnumerable(ServiceDescriptor.Transient(typeof(IObjectValidator<>).MakeGenericType(modelType),
validatorType));
services.Add(ServiceDescriptor.Transient(typeof(IObjectValidator<>).MakeGenericType(modelType),
provider => CreateValidator(provider, validatorType)));
// 注册 AbstractValidator<T> 基类
// services.TryAddEnumerable(
// ServiceDescriptor.Transient(typeof(AbstractValidator<>).MakeGenericType(modelType), validatorType));
// services.Add(ServiceDescriptor.Transient(typeof(AbstractValidator<>).MakeGenericType(modelType),
// provider => CreateValidator(provider, validatorType)));
// 注册 IObjectValidator 非泛型接口
services.TryAddEnumerable(ServiceDescriptor.Transient(typeof(IObjectValidator), validatorType));
services.Add(ServiceDescriptor.Transient(typeof(IObjectValidator),
provider => CreateValidator(provider, validatorType)));
}
}
/// <summary>
/// 创建对象验证器实例
/// </summary>
/// <param name="serviceProvider">
/// <see cref="IServiceProvider" />
/// </param>
/// <param name="validatorType">对象验证器类型</param>
/// <returns>
/// <see cref="IObjectValidator" />
/// </returns>
internal static IObjectValidator CreateValidator(IServiceProvider serviceProvider, Type validatorType)
{
// 空检查
ArgumentNullException.ThrowIfNull(serviceProvider);
ArgumentNullException.ThrowIfNull(validatorType);
// 创建对象验证器实例
var validator = (IObjectValidator)ActivatorUtilities.CreateInstance(serviceProvider, validatorType);
// 同步 IServiceProvider 委托
validator.InitializeServiceProvider(serviceProvider.GetService);
return validator;
}
/// <summary>
/// 检查是否继承自 <see cref="AbstractValidator{T}" /> 抽象基类
/// </summary>

View File

@@ -53,7 +53,7 @@ public static class ValidationExtensions
validationResults?.ToList().ToResults();
/// <summary>
/// 使用对象验证器验证当前实例并返回验证结果集合
/// 创建对象验证器验证当前实例并返回验证结果集合
/// </summary>
/// <param name="validationContext">
/// <see cref="ValidationContext" />
@@ -70,16 +70,44 @@ public static class ValidationExtensions
// 空检查
ArgumentNullException.ThrowIfNull(validationContext);
// 解析 IServiceProvider 服务
var serviceProvider = validationContext.GetService(typeof(IServiceProvider)) as IServiceProvider;
// 初始化 ObjectValidator<T> 实例
// 初始化 ObjectValidator<T> 实例并禁用属性验证特性验证,避免死循环
using var objectValidator = new ObjectValidator<T>(new ValidatorOptions { SuppressAnnotationValidation = true },
serviceProvider, validationContext.Items);
null, validationContext.Items);
// 调用自定义配置委托
configure?.Invoke(objectValidator);
return validationContext.ValidateWith(objectValidator);
}
/// <summary>
/// 使用指定对象验证器验证当前实例并返回验证结果集合
/// </summary>
/// <param name="validationContext">
/// <see cref="ValidationContext" />
/// </param>
/// <param name="objectValidator">
/// <see cref="AbstractValidator{T}" />
/// </param>
/// <typeparam name="T">对象类型</typeparam>
/// <returns>
/// <see cref="IEnumerable{T}" />
/// </returns>
public static IEnumerable<ValidationResult> ValidateWith<T>(this ValidationContext validationContext,
ObjectValidator<T> objectValidator) where T : class
{
// 空检查
ArgumentNullException.ThrowIfNull(validationContext);
// 禁用属性验证特性验证,避免死循环
objectValidator.ConfigureOptions(options =>
{
options.SuppressAnnotationValidation = true;
});
// 同步 IServiceProvider 委托
objectValidator.InitializeServiceProvider(validationContext.GetService);
// 尝试从 Items 中解析规则集列表
string?[]? ruleSets = null;
if (validationContext.Items.TryGetValue(Constants.RULESETS_KEY, out var ruleSetsObj))

View File

@@ -30,7 +30,7 @@ namespace Furion.Validation;
/// <summary>
/// 对象验证器服务
/// </summary>
public interface IObjectValidator;
public interface IObjectValidator : IValidatorInitializer;
/// <summary>
/// <inheritdoc cref="IObjectValidator" />

View File

@@ -49,8 +49,10 @@ public class ObjectValidator<T> : IObjectValidator<T>, IDisposable
/// </summary>
internal readonly Stack<string?> _ruleSetStack;
/// <inheritdoc cref="IServiceProvider" />
internal readonly IServiceProvider? _serviceProvider;
/// <summary>
/// <see cref="IServiceProvider" /> 委托
/// </summary>
internal Func<Type, object?>? _serviceProvider;
/// <summary>
/// <inheritdoc cref="ObjectValidator{T}" />
@@ -89,7 +91,13 @@ public class ObjectValidator<T> : IObjectValidator<T>, IDisposable
ArgumentNullException.ThrowIfNull(options);
Options = options;
_serviceProvider = serviceProvider;
// 空检查
if (serviceProvider is not null)
{
_serviceProvider = serviceProvider.GetService;
}
_items = items;
// 初始化 ObjectAnnotationValidator 实例
@@ -209,6 +217,21 @@ public class ObjectValidator<T> : IObjectValidator<T>, IDisposable
}
}
/// <inheritdoc />
public void InitializeServiceProvider(Func<Type, object?>? serviceProvider)
{
_serviceProvider = serviceProvider;
// 同步 _annotationValidator 实例 IServiceProvider 委托
_annotationValidator.InitializeServiceProvider(serviceProvider);
// 遍历所有属性验证器并同步 IServiceProvider 委托
foreach (var propertyValidator in PropertyValidators)
{
propertyValidator.InitializeServiceProvider(serviceProvider);
}
}
/// <summary>
/// 配置属性验证器
/// </summary>

View File

@@ -245,8 +245,7 @@ public sealed partial class PropertyValidator<T, TProperty> where T : class
var validatorProxy = new ValidatorProxy<T, TValidator>(instance => GetValue(instance),
constructorArgsFactory is null
? null
: instance =>
constructorArgsFactory(new ValidationContext<T>(instance, _serviceProvider, _items?.AsReadOnly())));
: instance => constructorArgsFactory(CreateValidationContext(instance)));
// 空检查
if (configure is not null)

View File

@@ -55,8 +55,7 @@ public sealed partial class
/// <see cref="ObjectValidator{T}" />
/// </param>
internal PropertyValidator(Expression<Func<T, TProperty?>> selector, ObjectValidator<T> objectValidator)
: base((objectValidator ?? throw new ArgumentNullException(nameof(objectValidator)))._serviceProvider,
objectValidator._items)
: base(null, (objectValidator ?? throw new ArgumentNullException(nameof(objectValidator)))._items)
{
// 空检查
ArgumentNullException.ThrowIfNull(selector);
@@ -65,8 +64,10 @@ public sealed partial class
_objectValidator = objectValidator;
// 初始化 PropertyAnnotationValidator 实例
_annotationValidator =
new PropertyAnnotationValidator<T, TProperty>(selector, _serviceProvider, objectValidator._items);
_annotationValidator = new PropertyAnnotationValidator<T, TProperty>(selector, null, objectValidator._items);
// 同步 IServiceProvider 委托
InitializeServiceProvider(objectValidator._serviceProvider);
}
/// <summary>
@@ -206,6 +207,16 @@ public sealed partial class
}
}
/// <inheritdoc />
public override void InitializeServiceProvider(Func<Type, object?>? serviceProvider)
{
// 同步基类 IServiceProvider 委托
base.InitializeServiceProvider(serviceProvider);
// 同步 _annotationValidator 实例 IServiceProvider 委托
_annotationValidator.InitializeServiceProvider(serviceProvider);
}
/// <summary>
/// 设置对象验证器
/// </summary>
@@ -220,6 +231,9 @@ public sealed partial class
{
_propertyValidator = validator;
// 同步 IServiceProvider 委托
_propertyValidator?.InitializeServiceProvider(_serviceProvider);
return this;
}
@@ -438,4 +452,22 @@ public sealed partial class
/// <see cref="string" />
/// </returns>
internal string GetDisplayName() => _annotationValidator.GetDisplayName(DisplayName);
/// <summary>
/// 创建 <see cref="ValidationContext{T}" /> 实例
/// </summary>
/// <param name="value">对象</param>
/// <returns>
/// <see cref="ValidationContext{T}" />
/// </returns>
internal ValidationContext<T> CreateValidationContext(T value)
{
// 初始化 ValidationContext 实例
var validationContext = new ValidationContext<T>(value, null, _items?.AsReadOnly());
// 同步 IServiceProvider 委托
validationContext.InitializeServiceProvider(_serviceProvider);
return validationContext;
}
}

View File

@@ -23,16 +23,19 @@
// 请访问 https://gitee.com/dotnetchina/Furion 获取更多关于 Furion 项目的许可证和版权信息。
// ------------------------------------------------------------------------
using Microsoft.Extensions.DependencyInjection;
namespace Furion.Validation;
/// <summary>
/// 验证上下文
/// </summary>
/// <typeparam name="T">对象类型</typeparam>
public sealed class ValidationContext<T>
public sealed class ValidationContext<T> : IValidatorInitializer
{
/// <summary>
/// <see cref="IServiceProvider" /> 委托
/// </summary>
internal Func<Type, object?>? _serviceProvider;
/// <summary>
/// <inheritdoc cref="ValidationContext{T}" />
/// </summary>
@@ -48,7 +51,13 @@ public sealed class ValidationContext<T>
ArgumentNullException.ThrowIfNull(instance);
Instance = instance;
ServiceProvider = serviceProvider;
// 空检查
if (serviceProvider is not null)
{
_serviceProvider = serviceProvider.GetService;
}
Items = items is not null ? new Dictionary<object, object?>(items) : new Dictionary<object, object?>();
}
@@ -57,14 +66,23 @@ public sealed class ValidationContext<T>
/// </summary>
public T Instance { get; }
/// <inheritdoc cref="IServiceProvider" />
public IServiceProvider? ServiceProvider { get; }
/// <summary>
/// 验证上下文数据
/// </summary>
public IReadOnlyDictionary<object, object?> Items { get; }
/// <inheritdoc />
public void InitializeServiceProvider(Func<Type, object?>? serviceProvider) => _serviceProvider = serviceProvider;
/// <summary>
/// 解析服务
/// </summary>
/// <param name="serviceType">服务类型</param>
/// <returns>
/// <see cref="object" />
/// </returns>
public object? GetService(Type serviceType) => _serviceProvider?.Invoke(serviceType);
/// <summary>
/// 解析服务
/// </summary>
@@ -72,5 +90,5 @@ public sealed class ValidationContext<T>
/// <returns>
/// <typeparamref name="TService" />
/// </returns>
public TService? GetService<TService>() where TService : class => ServiceProvider?.GetService<TService>();
public TService? GetService<TService>() where TService : class => (TService?)GetService(typeof(TService));
}

View File

@@ -30,7 +30,7 @@ namespace Furion.Validation;
/// <summary>
/// 组合验证器
/// </summary>
public class CompositeValidator : ValidatorBase
public class CompositeValidator : ValidatorBase, IValidatorInitializer
{
/// <summary>
/// 高优先级验证器列表
@@ -73,6 +73,19 @@ public class CompositeValidator : ValidatorBase
/// <remarks>默认值为:<see cref="ValidationMode.ValidateAll" />。</remarks>
public ValidationMode Mode { get; set; } = ValidationMode.ValidateAll;
/// <inheritdoc />
public void InitializeServiceProvider(Func<Type, object?>? serviceProvider)
{
// 遍历所有实现 IValidatorInitializer 接口的验证器并同步 IServiceProvider 委托
foreach (var validator in Validators)
{
if (validator is IValidatorInitializer initializer)
{
initializer.InitializeServiceProvider(serviceProvider);
}
}
}
/// <inheritdoc />
public override bool IsValid(object? value)
{

View File

@@ -31,7 +31,7 @@ namespace Furion.Validation;
/// 条件验证器
/// </summary>
/// <typeparam name="T">对象类型</typeparam>
public class ConditionalValidator<T> : ValidatorBase<T>
public class ConditionalValidator<T> : ValidatorBase<T>, IValidatorInitializer
{
/// <summary>
/// 条件和对应的验证器列表
@@ -64,6 +64,22 @@ public class ConditionalValidator<T> : ValidatorBase<T>
ErrorMessageResourceAccessor = () => null!;
}
/// <inheritdoc />
public void InitializeServiceProvider(Func<Type, object?>? serviceProvider)
{
// 组合所有条件的验证器
var validators = (_defaultValidators ?? []).Concat(_conditions.SelectMany(u => u.Validators));
// 遍历所有实现 IValidatorInitializer 接口的验证器并同步 IServiceProvider 委托
foreach (var validator in validators)
{
if (validator is IValidatorInitializer initializer)
{
initializer.InitializeServiceProvider(serviceProvider);
}
}
}
/// <inheritdoc />
public override bool IsValid(T? instance)
{

View File

@@ -0,0 +1,38 @@
// ------------------------------------------------------------------------
// 版权信息
// 版权归百小僧及百签科技(广东)有限公司所有。
// 所有权利保留。
// 官方网站https://baiqian.com
//
// 许可证信息
// Furion 项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
// 官方网站https://furion.net
//
// 使用条款
// 使用本代码应遵守相关法律法规和许可证的要求。
//
// 免责声明
// 对于因使用本代码而产生的任何直接、间接、偶然、特殊或后果性损害,我们不承担任何责任。
//
// 其他重要信息
// Furion 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。
// 有关 Furion 项目的其他详细信息,请参阅位于源代码树根目录中的 COPYRIGHT 和 DISCLAIMER 文件。
//
// 更多信息
// 请访问 https://gitee.com/dotnetchina/Furion 获取更多关于 Furion 项目的许可证和版权信息。
// ------------------------------------------------------------------------
namespace Furion.Validation;
/// <summary>
/// 定义验证器的 <see cref="IServiceProvider" /> 初始化行为
/// </summary>
public interface IValidatorInitializer
{
/// <summary>
/// 初始化(同步) <see cref="IServiceProvider" /> 委托
/// </summary>
/// <param name="serviceProvider"><see cref="IServiceProvider" /> 委托</param>
void InitializeServiceProvider(Func<Type, object?>? serviceProvider);
}

View File

@@ -31,20 +31,25 @@ namespace Furion.Validation;
/// 对象验证特性验证器
/// </summary>
/// <remarks>支持使用 <c>[ValidateNever]</c> 特性来跳过对特定属性的验证,仅限于 ASP.NET Core 应用项目。</remarks>
public class ObjectAnnotationValidator : ValidatorBase
public class ObjectAnnotationValidator : ValidatorBase, IValidatorInitializer
{
/// <summary>
/// 验证上下文数据
/// </summary>
internal readonly IDictionary<object, object?>? _items;
/// <inheritdoc cref="IServiceProvider" />
internal readonly IServiceProvider? _serviceProvider;
/// <summary>
/// <see cref="IServiceProvider" /> 委托
/// </summary>
internal Func<Type, object?>? _serviceProvider;
/// <summary>
/// <inheritdoc cref="ObjectAnnotationValidator" />
/// </summary>
public ObjectAnnotationValidator() => ErrorMessageResourceAccessor = () => null!;
public ObjectAnnotationValidator()
: this(null, null)
{
}
/// <summary>
/// <inheritdoc cref="ObjectAnnotationValidator" />
@@ -63,10 +68,15 @@ public class ObjectAnnotationValidator : ValidatorBase
/// </param>
/// <param name="items">验证上下文数据</param>
public ObjectAnnotationValidator(IServiceProvider? serviceProvider, IDictionary<object, object?>? items)
: this()
{
// 空检查
if (serviceProvider is not null)
{
_serviceProvider = serviceProvider.GetService;
}
_items = items;
_serviceProvider = serviceProvider;
ErrorMessageResourceAccessor = () => null!;
}
/// <summary>
@@ -79,14 +89,16 @@ public class ObjectAnnotationValidator : ValidatorBase
/// </remarks>
public bool ValidateAllProperties { get; set; } = true;
/// <inheritdoc />
public void InitializeServiceProvider(Func<Type, object?>? serviceProvider) => _serviceProvider = serviceProvider;
/// <inheritdoc />
public override bool IsValid(object? value)
{
// 空检查
ArgumentNullException.ThrowIfNull(value);
return Validator.TryValidateObject(value, new ValidationContext(value, _serviceProvider, _items), null,
ValidateAllProperties);
return Validator.TryValidateObject(value, CreateValidationContext(value), null, ValidateAllProperties);
}
/// <inheritdoc />
@@ -105,8 +117,7 @@ public class ObjectAnnotationValidator : ValidatorBase
* 参考源码:
* https://github.com/dotnet/runtime/blob/5535e31a712343a63f5d7d796cd874e563e5ac14/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/Validator.cs#L423-L430
*/
Validator.TryValidateObject(value, new ValidationContext(value, _serviceProvider, _items), validationResults,
ValidateAllProperties);
Validator.TryValidateObject(value, CreateValidationContext(value), validationResults, ValidateAllProperties);
// 如果验证未通过且配置了自定义错误信息,则在首部添加自定义错误信息
if (validationResults.Count > 0 && (string?)ErrorMessageString is not null)
@@ -125,8 +136,7 @@ public class ObjectAnnotationValidator : ValidatorBase
try
{
Validator.ValidateObject(value, new ValidationContext(value, _serviceProvider, _items),
ValidateAllProperties);
Validator.ValidateObject(value, CreateValidationContext(value), ValidateAllProperties);
}
// 如果验证未通过且配置了自定义错误信息,则重新抛出异常
catch (ValidationException e) when (ErrorMessageString is not null)
@@ -142,4 +152,22 @@ public class ObjectAnnotationValidator : ValidatorBase
/// <inheritdoc />
public override string? FormatErrorMessage(string name) =>
(string?)ErrorMessageString is null ? null : base.FormatErrorMessage(name);
/// <summary>
/// 创建 <see cref="ValidationContext" /> 实例
/// </summary>
/// <param name="value">对象</param>
/// <returns>
/// <see cref="ValidationContext" />
/// </returns>
internal ValidationContext CreateValidationContext(object value)
{
// 初始化 ValidationContext 实例
var validationContext = new ValidationContext(value, null, _items);
// 同步 IServiceProvider 委托
validationContext.InitializeServiceProvider(_serviceProvider!);
return validationContext;
}
}

View File

@@ -108,7 +108,7 @@ public class PropertyAnnotationValidator<T, TProperty> : PropertyAnnotationValid
/// 属性验证特性验证器
/// </summary>
/// <typeparam name="T">对象类型</typeparam>
public class PropertyAnnotationValidator<T> : ValidatorBase<T>
public class PropertyAnnotationValidator<T> : ValidatorBase<T>, IValidatorInitializer
where T : class
{
/// <summary>
@@ -121,22 +121,18 @@ public class PropertyAnnotationValidator<T> : ValidatorBase<T>
/// </summary>
internal readonly IDictionary<object, object?>? _items;
/// <inheritdoc cref="IServiceProvider" />
internal readonly IServiceProvider? _serviceProvider;
/// <summary>
/// <see cref="IServiceProvider" /> 委托
/// </summary>
internal Func<Type, object?>? _serviceProvider;
/// <summary>
/// <inheritdoc cref="PropertyAnnotationValidator{T}" />
/// </summary>
/// <param name="selector">属性选择器</param>
public PropertyAnnotationValidator(Expression<Func<T, object?>> selector)
: this(selector, null, null)
{
// 空检查
ArgumentNullException.ThrowIfNull(selector);
Property = selector.GetProperty();
ErrorMessageResourceAccessor = () => null!;
_getter = selector.Compile();
}
/// <summary>
@@ -159,10 +155,21 @@ public class PropertyAnnotationValidator<T> : ValidatorBase<T>
/// <param name="items">验证上下文数据</param>
public PropertyAnnotationValidator(Expression<Func<T, object?>> selector, IServiceProvider? serviceProvider,
IDictionary<object, object?>? items)
: this(selector)
{
// 空检查
ArgumentNullException.ThrowIfNull(selector);
Property = selector.GetProperty();
_getter = selector.Compile();
// 空检查
if (serviceProvider is not null)
{
_serviceProvider = serviceProvider.GetService;
}
_items = items;
_serviceProvider = serviceProvider;
ErrorMessageResourceAccessor = () => null!;
}
/// <summary>
@@ -170,14 +177,17 @@ public class PropertyAnnotationValidator<T> : ValidatorBase<T>
/// </summary>
public PropertyInfo Property { get; }
/// <inheritdoc />
public void InitializeServiceProvider(Func<Type, object?>? serviceProvider) => _serviceProvider = serviceProvider;
/// <inheritdoc />
public override bool IsValid(T? instance)
{
// 空检查
ArgumentNullException.ThrowIfNull(instance);
return Validator.TryValidateProperty(GetValue(instance),
new ValidationContext(instance, _serviceProvider, _items) { MemberName = Property.Name }, null);
return Validator.TryValidateProperty(GetValue(instance), CreateValidationContext(instance, Property.Name),
null);
}
/// <inheritdoc />
@@ -192,8 +202,8 @@ public class PropertyAnnotationValidator<T> : ValidatorBase<T>
// 初始化验证结果集合
var validationResults = new List<ValidationResult>();
Validator.TryValidateProperty(GetValue(instance),
new ValidationContext(instance, _serviceProvider, _items) { MemberName = propertyName }, validationResults);
Validator.TryValidateProperty(GetValue(instance), CreateValidationContext(instance, propertyName),
validationResults);
// 如果验证未通过且配置了自定义错误信息,则在首部添加自定义错误信息
if (validationResults.Count > 0 && (string?)ErrorMessageString is not null)
@@ -216,8 +226,7 @@ public class PropertyAnnotationValidator<T> : ValidatorBase<T>
try
{
Validator.ValidateProperty(GetValue(instance),
new ValidationContext(instance, _serviceProvider, _items) { MemberName = propertyName });
Validator.ValidateProperty(GetValue(instance), CreateValidationContext(instance, propertyName));
}
// 如果验证未通过且配置了自定义错误信息,则重新抛出异常
catch (ValidationException e) when (ErrorMessageString is not null)
@@ -256,4 +265,23 @@ public class PropertyAnnotationValidator<T> : ValidatorBase<T>
public string GetDisplayName(string? name) =>
name ?? Property.GetCustomAttribute<DisplayAttribute>(false)?.Name ??
Property.GetCustomAttribute<DisplayNameAttribute>(false)?.DisplayName ?? Property.Name;
/// <summary>
/// 创建 <see cref="ValidationContext" /> 实例
/// </summary>
/// <param name="value">对象</param>
/// <param name="memberName">成员名称</param>
/// <returns>
/// <see cref="ValidationContext" />
/// </returns>
internal ValidationContext CreateValidationContext(object value, string? memberName)
{
// 初始化 ValidationContext 实例
var validationContext = new ValidationContext(value, null, _items) { MemberName = memberName };
// 同步 IServiceProvider 委托
validationContext.InitializeServiceProvider(_serviceProvider!);
return validationContext;
}
}

View File

@@ -37,7 +37,7 @@ namespace Furion.Validation;
/// <typeparam name="TValidator">
/// <see cref="ValidatorBase" />
/// </typeparam>
public class ValidatorProxy<TValidator> : ValidatorBase, IDisposable
public class ValidatorProxy<TValidator> : ValidatorBase, IDisposable, IValidatorInitializer
where TValidator : ValidatorBase
{
/// <summary>
@@ -65,6 +65,17 @@ public class ValidatorProxy<TValidator> : ValidatorBase, IDisposable
GC.SuppressFinalize(this);
}
/// <inheritdoc />
public void InitializeServiceProvider(Func<Type, object?>? serviceProvider)
{
// 检查验证器是否实现 IValidatorInitializer 接口
if (Validator is IValidatorInitializer initializer)
{
// 同步 IServiceProvider 委托
initializer.InitializeServiceProvider(serviceProvider);
}
}
/// <summary>
/// 配置验证器实例
/// </summary>

View File

@@ -30,15 +30,17 @@ namespace Furion.Validation;
/// <summary>
/// 单个值验证特性验证器
/// </summary>
public class ValueAnnotationValidator : ValidatorBase
public class ValueAnnotationValidator : ValidatorBase, IValidatorInitializer
{
/// <summary>
/// 验证上下文数据
/// </summary>
internal readonly IDictionary<object, object?>? _items;
/// <inheritdoc cref="IServiceProvider" />
internal readonly IServiceProvider? _serviceProvider;
/// <summary>
/// <see cref="IServiceProvider" /> 委托
/// </summary>
internal Func<Type, object?>? _serviceProvider;
/// <summary>
/// <inheritdoc cref="ValueAnnotationValidator" />
@@ -46,19 +48,8 @@ public class ValueAnnotationValidator : ValidatorBase
/// <param name="attributes">验证特性列表</param>
/// <exception cref="ArgumentException"></exception>
public ValueAnnotationValidator(params ValidationAttribute[] attributes)
: this(attributes, null, null)
{
// 空检查
ArgumentNullException.ThrowIfNull(attributes);
// 确保数组元素不存在 null 值
if (attributes.Any(u => (ValidationAttribute?)u is null))
{
// ReSharper disable once LocalizableElement
throw new ArgumentException("Attributes cannot contain null elements.", nameof(attributes));
}
Attributes = attributes;
ErrorMessageResourceAccessor = () => null!;
}
/// <summary>
@@ -81,10 +72,27 @@ public class ValueAnnotationValidator : ValidatorBase
/// <param name="items">验证上下文数据</param>
public ValueAnnotationValidator(ValidationAttribute[] attributes, IServiceProvider? serviceProvider,
IDictionary<object, object?>? items)
: this(attributes)
{
// 空检查
ArgumentNullException.ThrowIfNull(attributes);
// 确保数组元素不存在 null 值
if (attributes.Any(u => (ValidationAttribute?)u is null))
{
// ReSharper disable once LocalizableElement
throw new ArgumentException("Attributes cannot contain null elements.", nameof(attributes));
}
Attributes = attributes;
// 空检查
if (serviceProvider is not null)
{
_serviceProvider = serviceProvider.GetService;
}
_items = items;
_serviceProvider = serviceProvider;
ErrorMessageResourceAccessor = () => null!;
}
/// <summary>
@@ -92,10 +100,12 @@ public class ValueAnnotationValidator : ValidatorBase
/// </summary>
public ValidationAttribute[] Attributes { get; }
/// <inheritdoc />
public void InitializeServiceProvider(Func<Type, object?>? serviceProvider) => _serviceProvider = serviceProvider;
/// <inheritdoc />
public override bool IsValid(object? value) =>
Validator.TryValidateValue(value, new ValidationContext(new object(), _serviceProvider, _items), null,
Attributes);
Validator.TryValidateValue(value, CreateValidationContext(new object(), null), null, Attributes);
/// <inheritdoc />
public override List<ValidationResult>? GetValidationResults(object? value, string name)
@@ -103,9 +113,8 @@ public class ValueAnnotationValidator : ValidatorBase
// 初始化属性名称和验证结果集合
var (memberName, validationResults) = (GetMemberName(name), new List<ValidationResult>());
Validator.TryValidateValue(value,
new ValidationContext(new object(), _serviceProvider, _items) { MemberName = memberName },
validationResults, Attributes);
Validator.TryValidateValue(value, CreateValidationContext(new object(), memberName), validationResults,
Attributes);
// 如果验证未通过且配置了自定义错误信息,则在首部添加自定义错误信息
if (validationResults.Count > 0 && (string?)ErrorMessageString is not null)
@@ -124,8 +133,7 @@ public class ValueAnnotationValidator : ValidatorBase
try
{
Validator.ValidateValue(value,
new ValidationContext(new object(), _serviceProvider, _items) { MemberName = memberName }, Attributes);
Validator.ValidateValue(value, CreateValidationContext(new object(), memberName), Attributes);
}
// 如果验证未通过且配置了自定义错误信息,则重新抛出异常
catch (ValidationException e) when (ErrorMessageString is not null)
@@ -147,4 +155,23 @@ public class ValueAnnotationValidator : ValidatorBase
/// <see cref="string" />
/// </returns>
internal static string GetMemberName(string name) => string.IsNullOrEmpty(name) ? "Value" : name;
/// <summary>
/// 创建 <see cref="ValidationContext" /> 实例
/// </summary>
/// <param name="value">对象</param>
/// <param name="memberName">成员名称</param>
/// <returns>
/// <see cref="ValidationContext" />
/// </returns>
internal ValidationContext CreateValidationContext(object value, string? memberName)
{
// 初始化 ValidationContext 实例
var validationContext = new ValidationContext(value, null, _items) { MemberName = memberName };
// 同步 IServiceProvider 委托
validationContext.InitializeServiceProvider(_serviceProvider!);
return validationContext;
}
}

View File

@@ -11,8 +11,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.7.220" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.220" />
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.7.221" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.221" />
</ItemGroup>
<ItemGroup>

View File

@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
<metadata>
<id>Furion.Template.Api</id>
<version>4.9.7.220</version>
<version>4.9.7.221</version>
<description>基于 Furion 框架快速搭建 Api 多层架构模板。</description>
<authors>百小僧</authors>
<packageTypes>

View File

@@ -12,9 +12,9 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Furion" Version="4.9.7.220" />
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.7.220" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.220" />
<PackageReference Include="Furion" Version="4.9.7.221" />
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.7.221" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.221" />
</ItemGroup>
</Project>

View File

@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
<metadata>
<id>Furion.Template.App</id>
<version>4.9.7.220</version>
<version>4.9.7.221</version>
<description>基于 Furion 框架快速搭建 Mvc/Api 多层架构模板。</description>
<authors>百小僧</authors>
<packageTypes>

View File

@@ -12,9 +12,9 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Furion" Version="4.9.7.220" />
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.7.220" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.220" />
<PackageReference Include="Furion" Version="4.9.7.221" />
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.7.221" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.221" />
</ItemGroup>
</Project>

View File

@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
<metadata>
<id>Furion.Template.Blazor.App</id>
<version>4.9.7.220</version>
<version>4.9.7.221</version>
<description>基于 Furion 框架快速搭建 Blazor App 多层架构模板。</description>
<authors>百小僧</authors>
<packageTypes>

View File

@@ -7,8 +7,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Furion" Version="4.9.7.220" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.220" />
<PackageReference Include="Furion" Version="4.9.7.221" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.221" />
</ItemGroup>
</Project>

View File

@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
<metadata>
<id>Furion.Template.Blazor</id>
<version>4.9.7.220</version>
<version>4.9.7.221</version>
<description>基于 Furion 框架快速搭建 Blazor 多层架构模板。</description>
<authors>百小僧</authors>
<packageTypes>

View File

@@ -7,8 +7,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Furion" Version="4.9.7.220" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.220" />
<PackageReference Include="Furion" Version="4.9.7.221" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.221" />
</ItemGroup>
</Project>

View File

@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
<metadata>
<id>Furion.Template.BlazorWithWebApi</id>
<version>4.9.7.220</version>
<version>4.9.7.221</version>
<description>基于 Furion 框架快速搭建 Blazor和WebApi 多层架构模板。</description>
<authors>百小僧</authors>
<packageTypes>

View File

@@ -12,9 +12,9 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Furion" Version="4.9.7.220" />
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.7.220" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.220" />
<PackageReference Include="Furion" Version="4.9.7.221" />
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.7.221" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.221" />
</ItemGroup>
</Project>

View File

@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
<metadata>
<id>Furion.Template.Mvc</id>
<version>4.9.7.220</version>
<version>4.9.7.221</version>
<description>基于 Furion 框架快速搭建 Mvc 多层架构模板。</description>
<authors>百小僧</authors>
<packageTypes>

View File

@@ -7,8 +7,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Furion" Version="4.9.7.220" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.220" />
<PackageReference Include="Furion" Version="4.9.7.221" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.221" />
</ItemGroup>
</Project>

View File

@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
<metadata>
<id>Furion.Template.Razor</id>
<version>4.9.7.220</version>
<version>4.9.7.221</version>
<description>基于 Furion 框架快速搭建 Razor Pages 多层架构模板。</description>
<authors>百小僧</authors>
<packageTypes>

View File

@@ -7,8 +7,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Furion" Version="4.9.7.220" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.220" />
<PackageReference Include="Furion" Version="4.9.7.221" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.221" />
</ItemGroup>
</Project>

View File

@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
<metadata>
<id>Furion.Template.RazorWithWebApi</id>
<version>4.9.7.220</version>
<version>4.9.7.221</version>
<description>基于 Furion 框架快速搭建 RazorPages和WebApi 多层架构模板。</description>
<authors>百小僧</authors>
<packageTypes>

View File

@@ -12,9 +12,9 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Furion" Version="4.9.7.220" />
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.7.220" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.220" />
<PackageReference Include="Furion" Version="4.9.7.221" />
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.7.221" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.221" />
</ItemGroup>
</Project>

View File

@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
<metadata>
<id>Furion.SqlSugar.Template.Api</id>
<version>4.9.7.220</version>
<version>4.9.7.221</version>
<description>基于 Furion 和 SqlSugar 框架快速搭建 Api 多层架构模板。</description>
<authors>百小僧</authors>
<packageTypes>

View File

@@ -12,9 +12,9 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.7.220" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.220" />
<PackageReference Include="Furion.Pure" Version="4.9.7.220" />
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.7.221" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.221" />
<PackageReference Include="Furion.Pure" Version="4.9.7.221" />
<PackageReference Include="SqlSugarCore" Version="5.1.4.210" />
</ItemGroup>

View File

@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
<metadata>
<id>Furion.SqlSugar.Template.App</id>
<version>4.9.7.220</version>
<version>4.9.7.221</version>
<description>基于 Furion 和 SqlSugar 框架快速搭建 Mvc/Api 多层架构模板。</description>
<authors>百小僧</authors>
<packageTypes>

View File

@@ -12,9 +12,9 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.7.220" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.220" />
<PackageReference Include="Furion.Pure" Version="4.9.7.220" />
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.7.221" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.221" />
<PackageReference Include="Furion.Pure" Version="4.9.7.221" />
<PackageReference Include="SqlSugarCore" Version="5.1.4.210" />
</ItemGroup>

View File

@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
<metadata>
<id>Furion.SqlSugar.Template.Blazor.App</id>
<version>4.9.7.220</version>
<version>4.9.7.221</version>
<description>基于 Furion 和 SqlSugar 框架快速搭建 Blazor App 多层架构模板。</description>
<authors>百小僧</authors>
<packageTypes>

View File

@@ -7,9 +7,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Furion.Pure" Version="4.9.7.220" />
<PackageReference Include="Furion.Pure" Version="4.9.7.221" />
<PackageReference Include="SqlSugarCore" Version="5.1.4.210" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.220" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.221" />
</ItemGroup>
</Project>

View File

@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
<metadata>
<id>Furion.SqlSugar.Template.Blazor</id>
<version>4.9.7.220</version>
<version>4.9.7.221</version>
<description>基于 Furion 和 SqlSugar 框架快速搭建 Blazor 多层架构模板。</description>
<authors>百小僧</authors>
<packageTypes>

View File

@@ -7,8 +7,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.220" />
<PackageReference Include="Furion.Pure" Version="4.9.7.220" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.221" />
<PackageReference Include="Furion.Pure" Version="4.9.7.221" />
<PackageReference Include="SqlSugarCore" Version="5.1.4.210" />
</ItemGroup>

View File

@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
<metadata>
<id>Furion.SqlSugar.Template.BlazorWithWebApi</id>
<version>4.9.7.220</version>
<version>4.9.7.221</version>
<description>基于 Furion 和 SqlSugar 框架快速搭建 Blazor和WebApi 多层架构模板。</description>
<authors>百小僧</authors>
<packageTypes>

View File

@@ -12,9 +12,9 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.7.220" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.220" />
<PackageReference Include="Furion.Pure" Version="4.9.7.220" />
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.7.221" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.221" />
<PackageReference Include="Furion.Pure" Version="4.9.7.221" />
<PackageReference Include="SqlSugarCore" Version="5.1.4.210" />
</ItemGroup>

View File

@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
<metadata>
<id>Furion.SqlSugar.Template.Mvc</id>
<version>4.9.7.220</version>
<version>4.9.7.221</version>
<description>基于 Furion 和 SqlSugar 框架快速搭建 Mvc 多层架构模板。</description>
<authors>百小僧</authors>
<packageTypes>

View File

@@ -7,8 +7,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.220" />
<PackageReference Include="Furion.Pure" Version="4.9.7.220" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.221" />
<PackageReference Include="Furion.Pure" Version="4.9.7.221" />
<PackageReference Include="SqlSugarCore" Version="5.1.4.210" />
</ItemGroup>

View File

@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
<metadata>
<id>Furion.SqlSugar.Template.Razor</id>
<version>4.9.7.220</version>
<version>4.9.7.221</version>
<description>基于 Furion 和 SqlSugar 框架快速搭建 Razor Pages 多层架构模板。</description>
<authors>百小僧</authors>
<packageTypes>

View File

@@ -7,8 +7,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.220" />
<PackageReference Include="Furion.Pure" Version="4.9.7.220" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.221" />
<PackageReference Include="Furion.Pure" Version="4.9.7.221" />
<PackageReference Include="SqlSugarCore" Version="5.1.4.210" />
</ItemGroup>

View File

@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
<metadata>
<id>Furion.SqlSugar.Template.RazorWithWebApi</id>
<version>4.9.7.220</version>
<version>4.9.7.221</version>
<description>基于 Furion 和 SqlSugar 框架快速搭建 RazorPages和WebApi 多层架构模板。</description>
<authors>百小僧</authors>
<packageTypes>

View File

@@ -12,9 +12,9 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.7.220" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.220" />
<PackageReference Include="Furion.Pure" Version="4.9.7.220" />
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.7.221" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.221" />
<PackageReference Include="Furion.Pure" Version="4.9.7.221" />
<PackageReference Include="SqlSugarCore" Version="5.1.4.210" />
</ItemGroup>

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<LangVersion>preview</LangVersion>
<TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks>
<Version>4.9.7.220</Version>
<Version>4.9.7.221</Version>
<ImplicitUsings>enable</ImplicitUsings>
<!--<Nullable>enable</Nullable>-->
<Authors>百小僧</Authors>

View File

@@ -7,7 +7,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Furion.Tools.CommandLine" Version="4.9.7.220" />
<PackageReference Include="Furion.Tools.CommandLine" Version="4.9.7.221" />
</ItemGroup>
</Project>

View File

@@ -96,7 +96,7 @@ function AddXmlCommentsToProperties($content, $commentsDictionary) {
return $modifiedContent
}
$FurTools = "Furion Tools v4.9.7.220";
$FurTools = "Furion Tools v4.9.7.221";
#
$copyright = @"