From 50d416dd25e0788cd96913c6fbfb5237078bd26d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Sat, 2 May 2020 02:44:54 +0300 Subject: [PATCH] Check validation on SetProperty. --- framework/Volo.Abp.sln | 11 +- .../Volo.Abp.ObjectExtending.csproj | 3 +- .../Abp/Data/HasExtraPropertiesExtensions.cs | 13 +- .../AbpObjectExtendingModule.cs | 8 +- .../ExtensibleObjectValidator.cs | 143 +++++++++++++++--- ...bjectExtensionPropertyValidationContext.cs | 1 + .../FodyWeavers.xml | 3 + .../FodyWeavers.xsd | 30 ++++ .../Volo.Abp.Validation.Abstractions.csproj | 21 +++ .../AbpValidationAbstractionsModule.cs | 9 ++ .../Abp/Validation/AbpValidationException.cs | 0 .../Abp/Validation/IHasValidationErrors.cs | 0 .../Volo.Abp.Validation.csproj | 1 + .../Abp/Validation/AbpValidationModule.cs | 1 + .../ExtensibleObjectValidator_Tests.cs | 35 +++-- nupkg/common.ps1 | 1 + 16 files changed, 243 insertions(+), 37 deletions(-) create mode 100644 framework/src/Volo.Abp.Validation.Abstractions/FodyWeavers.xml create mode 100644 framework/src/Volo.Abp.Validation.Abstractions/FodyWeavers.xsd create mode 100644 framework/src/Volo.Abp.Validation.Abstractions/Volo.Abp.Validation.Abstractions.csproj create mode 100644 framework/src/Volo.Abp.Validation.Abstractions/Volo/Abp/Validation/AbpValidationAbstractionsModule.cs rename framework/src/{Volo.Abp.Validation => Volo.Abp.Validation.Abstractions}/Volo/Abp/Validation/AbpValidationException.cs (100%) rename framework/src/{Volo.Abp.Validation => Volo.Abp.Validation.Abstractions}/Volo/Abp/Validation/IHasValidationErrors.cs (100%) diff --git a/framework/Volo.Abp.sln b/framework/Volo.Abp.sln index 5159f0a86e..5d6f3e99a2 100644 --- a/framework/Volo.Abp.sln +++ b/framework/Volo.Abp.sln @@ -279,9 +279,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.ObjectExtending", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.ObjectExtending.Tests", "test\Volo.Abp.ObjectExtending.Tests\Volo.Abp.ObjectExtending.Tests.csproj", "{17F8CA89-D9A2-4863-A5BD-B8E4D2901FD5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.TextTemplating", "src\Volo.Abp.TextTemplating\Volo.Abp.TextTemplating.csproj", "{9E53F91F-EACD-4191-A487-E727741F1311}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.TextTemplating", "src\Volo.Abp.TextTemplating\Volo.Abp.TextTemplating.csproj", "{9E53F91F-EACD-4191-A487-E727741F1311}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.TextTemplating.Tests", "test\Volo.Abp.TextTemplating.Tests\Volo.Abp.TextTemplating.Tests.csproj", "{251C7FD3-D313-4BCE-8068-352EC7EEA275}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.TextTemplating.Tests", "test\Volo.Abp.TextTemplating.Tests\Volo.Abp.TextTemplating.Tests.csproj", "{251C7FD3-D313-4BCE-8068-352EC7EEA275}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Validation.Abstractions", "src\Volo.Abp.Validation.Abstractions\Volo.Abp.Validation.Abstractions.csproj", "{FA5D1D6A-2A05-4A3D-99C1-2B6C1D1F99A3}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -841,6 +843,10 @@ Global {251C7FD3-D313-4BCE-8068-352EC7EEA275}.Debug|Any CPU.Build.0 = Debug|Any CPU {251C7FD3-D313-4BCE-8068-352EC7EEA275}.Release|Any CPU.ActiveCfg = Release|Any CPU {251C7FD3-D313-4BCE-8068-352EC7EEA275}.Release|Any CPU.Build.0 = Release|Any CPU + {FA5D1D6A-2A05-4A3D-99C1-2B6C1D1F99A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FA5D1D6A-2A05-4A3D-99C1-2B6C1D1F99A3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FA5D1D6A-2A05-4A3D-99C1-2B6C1D1F99A3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FA5D1D6A-2A05-4A3D-99C1-2B6C1D1F99A3}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -984,6 +990,7 @@ Global {17F8CA89-D9A2-4863-A5BD-B8E4D2901FD5} = {447C8A77-E5F0-4538-8687-7383196D04EA} {9E53F91F-EACD-4191-A487-E727741F1311} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {251C7FD3-D313-4BCE-8068-352EC7EEA275} = {447C8A77-E5F0-4538-8687-7383196D04EA} + {FA5D1D6A-2A05-4A3D-99C1-2B6C1D1F99A3} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5} diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo.Abp.ObjectExtending.csproj b/framework/src/Volo.Abp.ObjectExtending/Volo.Abp.ObjectExtending.csproj index e50ff26d5f..4b8542101a 100644 --- a/framework/src/Volo.Abp.ObjectExtending/Volo.Abp.ObjectExtending.csproj +++ b/framework/src/Volo.Abp.ObjectExtending/Volo.Abp.ObjectExtending.csproj @@ -1,4 +1,4 @@ - + @@ -15,6 +15,7 @@ + diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/HasExtraPropertiesExtensions.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/HasExtraPropertiesExtensions.cs index febf95bd22..ca9cc0a4c1 100644 --- a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/HasExtraPropertiesExtensions.cs +++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/HasExtraPropertiesExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using Volo.Abp.DynamicProxy; using Volo.Abp.ObjectExtending; using Volo.Abp.Reflection; @@ -37,10 +38,20 @@ namespace Volo.Abp.Data throw new AbpException("GetProperty does not support non-primitive types. Use non-generic GetProperty method and handle type casting manually."); } - public static TSource SetProperty(this TSource source, string name, object value) + public static TSource SetProperty( + this TSource source, + string name, + object value, + bool validate = true) where TSource : IHasExtraProperties { + if (validate) + { + ExtensibleObjectValidator.CheckValue(source, name, value); + } + source.ExtraProperties[name] = value; + return source; } diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/AbpObjectExtendingModule.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/AbpObjectExtendingModule.cs index f8b585594e..310e1feb23 100644 --- a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/AbpObjectExtendingModule.cs +++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/AbpObjectExtendingModule.cs @@ -1,10 +1,12 @@ -using Volo.Abp.Localization; -using Volo.Abp.Modularity; +using Volo.Abp.Modularity; +using Volo.Abp.Validation; +using Volo.Abp.Localization; namespace Volo.Abp.ObjectExtending { [DependsOn( - typeof(AbpLocalizationAbstractionsModule) + typeof(AbpLocalizationAbstractionsModule), + typeof(AbpValidationAbstractionsModule) )] public class AbpObjectExtendingModule : AbpModule { diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObjectValidator.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObjectValidator.cs index 5832b98cd3..d3fabc8c8e 100644 --- a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObjectValidator.cs +++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObjectValidator.cs @@ -4,18 +4,49 @@ using System.Linq; using JetBrains.Annotations; using Volo.Abp.Data; using Volo.Abp.DynamicProxy; +using Volo.Abp.Validation; namespace Volo.Abp.ObjectExtending { public static class ExtensibleObjectValidator { - [NotNull] + public static void CheckValue( + [NotNull] IHasExtraProperties extensibleObject, + [NotNull] string propertyName, + [CanBeNull] object value) + { + var validationErrors = GetValidationErrors( + extensibleObject, + propertyName, + value + ); + + if (validationErrors.Any()) + { + throw new AbpValidationException(validationErrors); + } + } + + public static bool IsValid( + [NotNull] IHasExtraProperties extensibleObject, + [CanBeNull] ValidationContext objectValidationContext = null) + { + return GetValidationErrors( + extensibleObject, + objectValidationContext + ).Any(); + } + public static bool IsValid( [NotNull] IHasExtraProperties extensibleObject, + [NotNull] string propertyName, + [CanBeNull] object value, [CanBeNull] ValidationContext objectValidationContext = null) { return GetValidationErrors( extensibleObject, + propertyName, + value, objectValidationContext ).Any(); } @@ -26,10 +57,30 @@ namespace Volo.Abp.ObjectExtending [CanBeNull] ValidationContext objectValidationContext = null) { var validationErrors = new List(); - + + AddValidationErrors( + extensibleObject, + validationErrors, + objectValidationContext + ); + + return validationErrors; + } + + [NotNull] + public static List GetValidationErrors( + [NotNull] IHasExtraProperties extensibleObject, + [NotNull] string propertyName, + [CanBeNull] object value, + [CanBeNull] ValidationContext objectValidationContext = null) + { + var validationErrors = new List(); + AddValidationErrors( extensibleObject, validationErrors, + propertyName, + value, objectValidationContext ); @@ -78,10 +129,55 @@ namespace Volo.Abp.ObjectExtending ); } + public static void AddValidationErrors( + [NotNull] IHasExtraProperties extensibleObject, + [NotNull] List validationErrors, + [NotNull] string propertyName, + [CanBeNull] object value, + [CanBeNull] ValidationContext objectValidationContext = null) + { + Check.NotNull(extensibleObject, nameof(extensibleObject)); + Check.NotNull(validationErrors, nameof(validationErrors)); + Check.NotNullOrWhiteSpace(propertyName, nameof(propertyName)); + + if (objectValidationContext == null) + { + objectValidationContext = new ValidationContext( + extensibleObject, + null, + new Dictionary() + ); + } + + var objectType = ProxyHelper.UnProxy(extensibleObject).GetType(); + + var objectExtensionInfo = ObjectExtensionManager.Instance + .GetOrNull(objectType); + + if (objectExtensionInfo == null) + { + return; + } + + var property = objectExtensionInfo.GetPropertyOrNull(propertyName); + if (property == null) + { + return; + } + + AddPropertyValidationErrors( + extensibleObject, + validationErrors, + objectValidationContext, + property, + value + ); + } + private static void AddPropertyValidationErrors( - IHasExtraProperties extensibleObject, + IHasExtraProperties extensibleObject, List validationErrors, - ValidationContext objectValidationContext, + ValidationContext objectValidationContext, ObjectExtensionInfo objectExtensionInfo) { var properties = objectExtensionInfo.GetProperties(); @@ -92,36 +188,46 @@ namespace Volo.Abp.ObjectExtending foreach (var property in properties) { - AddPropertyValidationErrors(extensibleObject, validationErrors, objectValidationContext, property); + AddPropertyValidationErrors( + extensibleObject, + validationErrors, + objectValidationContext, + property, + extensibleObject.GetProperty(property.Name) + ); } } private static void AddPropertyValidationErrors( - IHasExtraProperties extensibleObject, + IHasExtraProperties extensibleObject, List validationErrors, - ValidationContext objectValidationContext, - ObjectExtensionPropertyInfo property) + ValidationContext objectValidationContext, + ObjectExtensionPropertyInfo property, + object value) { AddPropertyValidationAttributeErrors( extensibleObject, validationErrors, objectValidationContext, - property + property, + value ); ExecuteCustomPropertyValidationActions( extensibleObject, validationErrors, objectValidationContext, - property + property, + value ); } private static void AddPropertyValidationAttributeErrors( - IHasExtraProperties extensibleObject, + IHasExtraProperties extensibleObject, List validationErrors, - ValidationContext objectValidationContext, - ObjectExtensionPropertyInfo property) + ValidationContext objectValidationContext, + ObjectExtensionPropertyInfo property, + object value) { var validationAttributes = property.GetValidationAttributes(); @@ -139,7 +245,7 @@ namespace Volo.Abp.ObjectExtending foreach (var attribute in validationAttributes) { var result = attribute.GetValidationResult( - extensibleObject.GetProperty(property.Name), + value, propertyValidationContext ); @@ -154,7 +260,8 @@ namespace Volo.Abp.ObjectExtending IHasExtraProperties extensibleObject, List validationErrors, ValidationContext objectValidationContext, - ObjectExtensionPropertyInfo property) + ObjectExtensionPropertyInfo property, + object value) { if (!property.Validators.Any()) { @@ -166,7 +273,7 @@ namespace Volo.Abp.ObjectExtending extensibleObject, validationErrors, objectValidationContext, - extensibleObject.GetProperty(property.Name) + value ); foreach (var validator in property.Validators) @@ -176,9 +283,9 @@ namespace Volo.Abp.ObjectExtending } private static void ExecuteCustomObjectValidationActions( - IHasExtraProperties extensibleObject, + IHasExtraProperties extensibleObject, List validationErrors, - ValidationContext objectValidationContext, + ValidationContext objectValidationContext, ObjectExtensionInfo objectExtensionInfo) { if (!objectExtensionInfo.Validators.Any()) diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionPropertyValidationContext.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionPropertyValidationContext.cs index ab2a7ccb38..81b6edbed8 100644 --- a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionPropertyValidationContext.cs +++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionPropertyValidationContext.cs @@ -40,6 +40,7 @@ namespace Volo.Abp.ObjectExtending /// /// Can be used to resolve services from the dependency injection container. + /// This can be null when SetProperty method is used on the object. /// [CanBeNull] public IServiceProvider ServiceProvider => ValidationContext; diff --git a/framework/src/Volo.Abp.Validation.Abstractions/FodyWeavers.xml b/framework/src/Volo.Abp.Validation.Abstractions/FodyWeavers.xml new file mode 100644 index 0000000000..be0de3a908 --- /dev/null +++ b/framework/src/Volo.Abp.Validation.Abstractions/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.Validation.Abstractions/FodyWeavers.xsd b/framework/src/Volo.Abp.Validation.Abstractions/FodyWeavers.xsd new file mode 100644 index 0000000000..3f3946e282 --- /dev/null +++ b/framework/src/Volo.Abp.Validation.Abstractions/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.Validation.Abstractions/Volo.Abp.Validation.Abstractions.csproj b/framework/src/Volo.Abp.Validation.Abstractions/Volo.Abp.Validation.Abstractions.csproj new file mode 100644 index 0000000000..2ed6955187 --- /dev/null +++ b/framework/src/Volo.Abp.Validation.Abstractions/Volo.Abp.Validation.Abstractions.csproj @@ -0,0 +1,21 @@ + + + + + + + netstandard2.0 + Volo.Abp.Validation.Abstractions + Volo.Abp.Validation.Abstractions + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.Validation.Abstractions/Volo/Abp/Validation/AbpValidationAbstractionsModule.cs b/framework/src/Volo.Abp.Validation.Abstractions/Volo/Abp/Validation/AbpValidationAbstractionsModule.cs new file mode 100644 index 0000000000..433ad86a4f --- /dev/null +++ b/framework/src/Volo.Abp.Validation.Abstractions/Volo/Abp/Validation/AbpValidationAbstractionsModule.cs @@ -0,0 +1,9 @@ +using Volo.Abp.Modularity; + +namespace Volo.Abp.Validation +{ + public class AbpValidationAbstractionsModule : AbpModule + { + + } +} diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/AbpValidationException.cs b/framework/src/Volo.Abp.Validation.Abstractions/Volo/Abp/Validation/AbpValidationException.cs similarity index 100% rename from framework/src/Volo.Abp.Validation/Volo/Abp/Validation/AbpValidationException.cs rename to framework/src/Volo.Abp.Validation.Abstractions/Volo/Abp/Validation/AbpValidationException.cs diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IHasValidationErrors.cs b/framework/src/Volo.Abp.Validation.Abstractions/Volo/Abp/Validation/IHasValidationErrors.cs similarity index 100% rename from framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IHasValidationErrors.cs rename to framework/src/Volo.Abp.Validation.Abstractions/Volo/Abp/Validation/IHasValidationErrors.cs diff --git a/framework/src/Volo.Abp.Validation/Volo.Abp.Validation.csproj b/framework/src/Volo.Abp.Validation/Volo.Abp.Validation.csproj index edbcf2f04d..10daff842e 100644 --- a/framework/src/Volo.Abp.Validation/Volo.Abp.Validation.csproj +++ b/framework/src/Volo.Abp.Validation/Volo.Abp.Validation.csproj @@ -21,6 +21,7 @@ + diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/AbpValidationModule.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/AbpValidationModule.cs index 9da6766a5d..3fb027cba7 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/AbpValidationModule.cs +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/AbpValidationModule.cs @@ -9,6 +9,7 @@ using Volo.Abp.VirtualFileSystem; namespace Volo.Abp.Validation { [DependsOn( + typeof(AbpValidationAbstractionsModule), typeof(AbpLocalizationModule) )] public class AbpValidationModule : AbpModule diff --git a/framework/test/Volo.Abp.ObjectExtending.Tests/Volo/Abp/ObjectExtending/ExtensibleObjectValidator_Tests.cs b/framework/test/Volo.Abp.ObjectExtending.Tests/Volo/Abp/ObjectExtending/ExtensibleObjectValidator_Tests.cs index d08e3766df..c41ffead56 100644 --- a/framework/test/Volo.Abp.ObjectExtending.Tests/Volo/Abp/ObjectExtending/ExtensibleObjectValidator_Tests.cs +++ b/framework/test/Volo.Abp.ObjectExtending.Tests/Volo/Abp/ObjectExtending/ExtensibleObjectValidator_Tests.cs @@ -2,6 +2,7 @@ using Shouldly; using Volo.Abp.Data; using Volo.Abp.Threading; +using Volo.Abp.Validation; using Xunit; namespace Volo.Abp.ObjectExtending @@ -85,8 +86,8 @@ namespace Volo.Abp.ObjectExtending ExtensibleObjectValidator .GetValidationErrors( new ExtensiblePersonObject() - .SetProperty("Name", "John") - .SetProperty("Age", 42) + .SetProperty("Name", "John", validate: false) + .SetProperty("Age", 42, validate: false) ).Count.ShouldBe(0); //All Valid } @@ -101,39 +102,49 @@ namespace Volo.Abp.ObjectExtending ExtensibleObjectValidator .GetValidationErrors( new ExtensiblePersonObject() - .SetProperty("Address", new string('x', 256)) + .SetProperty("Address", new string('x', 256), validate: false) ).Count.ShouldBe(3); // Name, Age & Address ExtensibleObjectValidator .GetValidationErrors( new ExtensiblePersonObject() - .SetProperty("Age", 42) + .SetProperty("Age", 42, validate: false) ).Count.ShouldBe(1); // Name ExtensibleObjectValidator .GetValidationErrors( new ExtensiblePersonObject() - .SetProperty("Address", new string('x', 256)) - .SetProperty("Age", 100) + .SetProperty("Address", new string('x', 256), validate: false) + .SetProperty("Age", 100, validate: false) ).Count.ShouldBe(3); // Name, Age & Address ExtensibleObjectValidator .GetValidationErrors( new ExtensiblePersonObject() - .SetProperty("Name", "John") - .SetProperty("Age", 42) - .SetProperty("Password", "123") - .SetProperty("PasswordRepeat", "1256") + .SetProperty("Name", "John", validate: false) + .SetProperty("Age", 42, validate: false) + .SetProperty("Password", "123", validate: false) + .SetProperty("PasswordRepeat", "1256", validate: false) ).Count.ShouldBe(1); // PasswordRepeat != Password ExtensibleObjectValidator .GetValidationErrors( new ExtensiblePersonObject() - .SetProperty("Name", "BadValue") - .SetProperty("Age", 42) + .SetProperty("Name", "BadValue", validate: false) + .SetProperty("Age", 42, validate: false) ).Count.ShouldBe(1); //Name is 'BadValue'! } + [Fact] + public void Should_Check_Validation_On_SetProperty() + { + Assert.Throws(() => + { + new ExtensiblePersonObject() + .SetProperty("Address", new string('x', 256)); + }); + } + private class ExtensiblePersonObject : ExtensibleObject { diff --git a/nupkg/common.ps1 b/nupkg/common.ps1 index 9c77b05eef..6dfe0041aa 100644 --- a/nupkg/common.ps1 +++ b/nupkg/common.ps1 @@ -107,6 +107,7 @@ $projects = ( "framework/src/Volo.Abp.UI", "framework/src/Volo.Abp.UI.Navigation", "framework/src/Volo.Abp.Uow", + "framework/src/Volo.Abp.Validation.Abstractions", "framework/src/Volo.Abp.Validation", "framework/src/Volo.Abp.VirtualFileSystem",