├── .gitignore ├── CommonAssemblyInfo.cs ├── Directory.Build.props ├── DynamicVNET.Lib.Benchmarks ├── DynamicVNET.Lib.Benchmarks.csproj ├── Helper │ ├── Sample.cs │ └── SampleSetup.cs ├── Program.cs └── ValidatorBenchmark.cs ├── DynamicVNET.Lib.Integration.Tests ├── DefaultApplierTest.cs ├── DynamicVNET.Lib.Integration.Tests.csproj ├── FailFirstApplierTest.cs ├── ModelValidatorTest.cs ├── RuleMarkerTest.cs ├── Stub │ ├── CustomModelValidator.cs │ ├── CustomStringValidator.cs │ ├── TokenStub.cs │ ├── UserStub.cs │ ├── UserStubST.cs │ └── UserStubStrongValidator.cs └── ValidatorTest.cs ├── DynamicVNET.Lib.Unit.Tests ├── DynamicVNET.Lib.Unit.Tests.csproj ├── ExpressionMemberTests.cs ├── RuleMarkerTests.cs └── Stub │ ├── TokenStub.cs │ ├── UserStub.cs │ └── UserStubST.cs ├── DynamicVNET.Lib ├── Core │ ├── Exceptions │ │ ├── ValidationException.cs │ │ └── ValidationMarkerException.cs │ ├── Internal │ │ ├── Applier │ │ │ ├── Applier.cs │ │ │ ├── DefaultApplier.cs │ │ │ └── FailFirstApplier.cs │ │ ├── Member │ │ │ ├── EmptyMember.cs │ │ │ ├── ExpressionMember.cs │ │ │ ├── ExpressionMemberFactory.cs │ │ │ └── IMember.cs │ │ ├── Rule │ │ │ ├── AnnotationRuleAdapter.cs │ │ │ ├── BaseRule.cs │ │ │ ├── BranchRule.cs │ │ │ ├── Context │ │ │ │ └── RuleContext.cs │ │ │ ├── IValidation.cs │ │ │ ├── IValidationRule.cs │ │ │ ├── PredicateRule.cs │ │ │ └── UniquePredicateRule.cs │ │ ├── RuleCollection.cs │ │ └── ValidationRuleResult.cs │ ├── Marker │ │ ├── BaseRuleMarker.cs │ │ ├── IMemberRuleMarker.cs │ │ ├── IRuleMarker.cs │ │ ├── ITypeRuleMarker.cs │ │ └── RuleMarker.cs │ ├── RuleExtensions.cs │ └── Validator │ │ ├── BaseValidator.cs │ │ ├── Common │ │ ├── IValidator.cs │ │ ├── MarkerSetup.cs │ │ └── ValidatorContext.cs │ │ ├── ProfileValidator.cs │ │ ├── Validator.cs │ │ └── ValidatorFactory.cs ├── DynamicVNET.Lib.csproj ├── ExtendAssemblyInfo.cs ├── Helper │ └── Utility.cs ├── MemberRuleExtensions.cs └── TypeRuleExtensions.cs ├── DynamicVNET.sln ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ 331 | -------------------------------------------------------------------------------- /CommonAssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | 4 | [assembly: InternalsVisibleTo("DynamicVNET.Lib.Unit.Tests")] 5 | [assembly: InternalsVisibleTo("DynamicVNET.Lib.Integration.Tests")] 6 | 7 | [assembly: AssemblyVersion("1.4.0")] 8 | [assembly: AssemblyInformationalVersion("1.4.0-beta")] 9 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | en 5 | false 6 | false 7 | $(MSBuildProjectDirectory)/../../.build/packages 8 | DynamicVNET 9 | 1.4.0 10 | 1.4.0 11 | Rasul Huseynov 12 | 13 | DynamicVNET is a .NET Standard library that was created to develop dynamic reuse validation. The main idea of the library is to apply validation rules using a declarative approach. And the rules can be used on POCO and BlackBox libraries. Also, it has rich facilities and features as Fluent API at runtime. 14 | 15 | DynamicVNET.Lib 16 | Copyright (c) 2018-2024 Rasul Huseynov 17 | https://github.com/rasulhsn/DynamicVNET/blob/master/LICENSE 18 | https://github.com/rasulhsn/DynamicVNET 19 | https://github.com/rasulhsn/DynamicVNET 20 | POCOValidation DataAnnotation Validator BindingDynamicValidation DynamicValidator FluentValidation 21 | True 22 | DynamicVNET Icon.png 23 | https://github.com/rasulhsn/DynamicVNET/blob/master/LICENSE 24 | https://github.com/rasulhsn/DynamicVNET/blob/master/README.md 25 | 26 | https://github.com/rasulhsn/DynamicVNET/blob/master/README.md 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /DynamicVNET.Lib.Benchmarks/DynamicVNET.Lib.Benchmarks.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | false 9 | DynamicVNET.Lib.Benchmarks 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /DynamicVNET.Lib.Benchmarks/Helper/Sample.cs: -------------------------------------------------------------------------------- 1 | namespace DynamicVNET.Lib.Benchmarks.Helper 2 | { 3 | public class SampleToken 4 | { 5 | public SampleToken InnerToken { get; set; } 6 | public string TokenNumber { get; set; } 7 | } 8 | public class Sample 9 | { 10 | public int Age { get; set; } 11 | public string Name { get; set; } 12 | public string Surname { get; set; } 13 | public SampleToken Token { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /DynamicVNET.Lib.Benchmarks/Helper/SampleSetup.cs: -------------------------------------------------------------------------------- 1 | using Bogus; 2 | 3 | namespace DynamicVNET.Lib.Benchmarks.Helper 4 | { 5 | public static class SampleSetup 6 | { 7 | public static (IValidator Validator, Sample Instance) CreateErrorType() 8 | { 9 | var sampleToken = CreateSampleToken(); 10 | 11 | var errorModelFaker = new Faker() 12 | .RuleFor(m => m.Age, m => m.Random.Int(0, 5)) 13 | .RuleFor(m => m.Name, m => m.Lorem.Word()); 14 | 15 | var ErrorModel = errorModelFaker.Generate(); 16 | ErrorModel.Token = sampleToken; 17 | 18 | var validator = ValidatorFactory.Create(builder => 19 | { 20 | builder 21 | .Range(x => x.Age, 10, 30) 22 | .Required(x => x.Name) 23 | .Required(x => x.Surname) 24 | .Predicate(x => 25 | { 26 | if (!string.IsNullOrEmpty(x.Surname) && x.Surname.Contains("fakeSample")) 27 | return true; 28 | return false; 29 | }) 30 | .Null(x => x.Token) 31 | .Null(x => x.Token.TokenNumber) 32 | .Null(x => x.Token.InnerToken.TokenNumber) 33 | .Null(x => x.Token.InnerToken.InnerToken.TokenNumber); 34 | }, failFirst: true); 35 | 36 | return (validator, ErrorModel); 37 | } 38 | 39 | public static (IValidator Validator, Sample Instance) CreateNonErrorType() 40 | { 41 | var sampleToken = CreateSampleToken(); 42 | 43 | var modelFaker = new Faker() 44 | .RuleFor(m => m.Age, m => m.Random.Int(10, 30)) 45 | .RuleFor(m => m.Name, m => m.Lorem.Word()) 46 | .RuleFor(m => m.Surname, m => "fakeSample"); 47 | 48 | var NoErrorModel = modelFaker.Generate(); 49 | NoErrorModel.Token = sampleToken; 50 | 51 | var validator = ValidatorFactory.Create(builder => 52 | { 53 | builder 54 | .Range(x => x.Age, 10, 30) 55 | .Required(x => x.Name) 56 | .Required(x => x.Surname) 57 | .Predicate(x => 58 | { 59 | if (!string.IsNullOrEmpty(x.Surname) && x.Surname.Contains("fakeSample")) 60 | return true; 61 | return false; 62 | }) 63 | .NotNull(x => x.Token) 64 | .NotNull(x => x.Token.TokenNumber) 65 | .NotNull(x => x.Token.InnerToken.TokenNumber) 66 | .NotNull(x => x.Token.InnerToken.InnerToken.TokenNumber); 67 | }); 68 | 69 | return (validator, NoErrorModel); 70 | } 71 | 72 | private static SampleToken CreateSampleToken() 73 | { 74 | var tokenFaker = new Faker() 75 | .RuleFor(x => x.TokenNumber, m => m.Random.Guid().ToString()); 76 | 77 | SampleToken tokenSample = tokenFaker.Generate(); 78 | tokenSample.InnerToken = tokenFaker.Generate(); 79 | tokenSample.InnerToken.InnerToken = tokenFaker.Generate(); 80 | 81 | return tokenSample; 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /DynamicVNET.Lib.Benchmarks/Program.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Running; 2 | 3 | namespace DynamicVNET.Lib.Benchmarks 4 | { 5 | class Program 6 | { 7 | static void Main(string[] args) 8 | => BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /DynamicVNET.Lib.Benchmarks/ValidatorBenchmark.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using DynamicVNET.Lib.Benchmarks.Helper; 3 | 4 | namespace DynamicVNET.Lib.Benchmarks 5 | { 6 | [MemoryDiagnoser] 7 | public class ValidatorBenchmark 8 | { 9 | private (IValidator Validator, Sample Instance) _validator; 10 | private (IValidator Validator, Sample Instance) _failFastValidator; 11 | 12 | [GlobalSetup] 13 | public void GlobalSetup() 14 | { 15 | _validator = SampleSetup.CreateNonErrorType(); 16 | _failFastValidator = SampleSetup.CreateErrorType(); 17 | } 18 | 19 | [Benchmark] 20 | public object FailValidate() 21 | { 22 | var model = _failFastValidator.Instance; 23 | 24 | object vResult = null; 25 | 26 | vResult = _failFastValidator.Validator.Validate(model); 27 | 28 | return vResult; 29 | } 30 | 31 | [Benchmark] 32 | public object Validate() 33 | { 34 | var model = _validator.Instance; 35 | 36 | var vResult = new object(); 37 | 38 | vResult = _validator.Validator.Validate(model); 39 | 40 | return vResult; 41 | } 42 | 43 | [Benchmark] 44 | public object FailIsValid() 45 | { 46 | var model = _failFastValidator.Instance; 47 | 48 | object vResult = null; 49 | 50 | vResult = _failFastValidator.Validator.IsValid(model); 51 | 52 | return vResult; 53 | } 54 | 55 | [Benchmark] 56 | public object IsValid() 57 | { 58 | var model = _validator.Instance; 59 | 60 | var vResult = new object(); 61 | 62 | vResult = _validator.Validator.IsValid(model); 63 | 64 | return vResult; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /DynamicVNET.Lib.Integration.Tests/DefaultApplierTest.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using DynamicVNET.Lib.Integration.Tests.Stub; 4 | using DynamicVNET.Lib.Internal; 5 | using Xunit; 6 | 7 | namespace DynamicVNET.Lib.Integration.Tests 8 | { 9 | /// 10 | /// 11 | /// 12 | public class DefaultApplierTest 13 | { 14 | private readonly UserStub _stub; 15 | 16 | /// 17 | /// Initializes a new instance of the class. 18 | /// 19 | public DefaultApplierTest() 20 | { 21 | _stub = new UserStub() 22 | { 23 | Age = 30, 24 | Name = "NameTest", 25 | Surname = "SurnameTest", 26 | Token = new TokenStub() 27 | { 28 | InnerToken = new TokenStub() 29 | { 30 | TokenNumber = "11111" 31 | } 32 | }, 33 | }; 34 | } 35 | 36 | private IEnumerable CreateRules() 37 | { 38 | RuleMarker marker = new RuleMarker(); 39 | marker.For(x => x.Name).Required().StringLen(10); 40 | marker.For(x => x.Surname).Required().StringLen(6); 41 | 42 | return marker.Rules; 43 | } 44 | 45 | [Fact] 46 | public void Apply_WhenGivenRightMarkerWithInstance_ShouldReturnAllValidationRules() 47 | { 48 | var rules = CreateRules(); 49 | var applier = new DefaultApplier(); 50 | 51 | var actual = applier.Apply(rules, _stub); 52 | 53 | Assert.True(actual.Count() == 4); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /DynamicVNET.Lib.Integration.Tests/DynamicVNET.Lib.Integration.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | false 9 | true 10 | 11 | DynamicVNET.Lib.Integration.Tests 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /DynamicVNET.Lib.Integration.Tests/FailFirstApplierTest.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using DynamicVNET.Lib.Integration.Tests.Stub; 4 | using DynamicVNET.Lib.Internal; 5 | using Xunit; 6 | 7 | namespace DynamicVNET.Lib.Integration.Tests 8 | { 9 | /// 10 | /// 11 | /// 12 | public class FailFirstApplierTest 13 | { 14 | private UserStub _stub; 15 | 16 | /// 17 | /// Initializes a new instance of the class. 18 | /// 19 | public FailFirstApplierTest() 20 | { 21 | _stub = new UserStub() 22 | { 23 | Age = 30, 24 | Name = "NameTest", 25 | Surname = "SurnameTest", 26 | Token = new TokenStub() 27 | { 28 | InnerToken = new TokenStub() 29 | { 30 | TokenNumber = "11111" 31 | } 32 | }, 33 | }; 34 | } 35 | 36 | private IEnumerable CreateRules() 37 | { 38 | RuleMarker marker = new RuleMarker(); 39 | marker.For(x => x.Name).Required().StringLen(10); 40 | marker.For(x => x.Surname).Required().StringLen(6); 41 | 42 | return marker.Rules; 43 | } 44 | 45 | [Fact] 46 | public void Apply_GivenConditionForOnlyInvalid_ReturnAllValidationRulesResult() 47 | { 48 | var rules = CreateRules(); 49 | var applier = new FailFirstApplier(); 50 | 51 | var actual = applier.Apply(rules, _stub); 52 | 53 | Assert.True(actual.Count() == 1); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /DynamicVNET.Lib.Integration.Tests/ModelValidatorTest.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using DynamicVNET.Lib.Integration.Tests.Stub; 3 | using Xunit; 4 | 5 | namespace DynamicVNET.Lib.Integration.Tests 6 | { 7 | public class ModelValidatorTest 8 | { 9 | [Fact] 10 | public void Example() 11 | { 12 | UserStub model = new UserStub() 13 | { 14 | Age = 12, 15 | Name = "a", 16 | Surname = "b", 17 | Token = new TokenStub() { TokenNumber = "AAAAAAAAAAAAAAAAA" } 18 | }; 19 | var validator = new ModelValidator(); 20 | 21 | bool actual = validator.IsValid(model); 22 | var result = validator.Validate(model); 23 | 24 | Assert.False(actual); 25 | Assert.True(result != null && result.Count() == 4); 26 | } 27 | 28 | [Fact] 29 | public void Example2() 30 | { 31 | UserStub model = new UserStub() 32 | { 33 | Age = 19, 34 | Name = "a", 35 | Surname = "b", 36 | Token = new TokenStub() { TokenNumber = "resul@gmail" } 37 | }; 38 | var validator = new ModelValidator(); 39 | 40 | bool actual = validator.IsValid(model); 41 | 42 | Assert.False(actual); 43 | } 44 | 45 | 46 | [Fact] 47 | public void Example3() 48 | { 49 | UserStub model = new UserStub() 50 | { 51 | Age = 19, 52 | Name = "a", 53 | Surname = "b", 54 | Token = new TokenStub() { TokenNumber = "resul@gmail.com" } 55 | }; 56 | var validator = new ModelValidator(); 57 | 58 | bool actual = validator.IsValid(model); 59 | 60 | Assert.True(actual); 61 | } 62 | 63 | [Fact] 64 | public void Example4() 65 | { 66 | string value = "aaaa"; 67 | var validator = new StringValidator(); 68 | 69 | bool actual = validator.IsValid(value); 70 | 71 | Assert.True(actual); 72 | } 73 | 74 | 75 | [Fact] 76 | public void Example5() 77 | { 78 | UserStub model = new UserStub() 79 | { 80 | Age = 20, 81 | Name = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 82 | Surname = "b", 83 | Token = new TokenStub() { TokenNumber = "resul@gmail.com" } 84 | }; 85 | var validator = new ModelValidator(); 86 | 87 | bool actual = validator.IsValid(model); 88 | 89 | Assert.False(actual); 90 | } 91 | 92 | [Fact] 93 | public void Example6() 94 | { 95 | string value = "HuseynovHuseynov"; 96 | var validator = new StringValidator(); 97 | 98 | bool actual = validator.IsValid(value); 99 | 100 | Assert.False(actual); 101 | } 102 | 103 | [Fact] 104 | public void ExampleWithFailFast8() 105 | { 106 | UserStub model = new UserStub() 107 | { 108 | Age = 12, 109 | Name = "a", 110 | Surname = "b", 111 | Token = new TokenStub() { TokenNumber = "AAAAAAAAAAAAAAAAA" } 112 | }; 113 | var validator = new UserStubStrongValidator(); 114 | 115 | var result = validator.Validate(model); 116 | 117 | Assert.True(result != null && result.Count() == 1); 118 | } 119 | 120 | [Fact] 121 | public void Example9() 122 | { 123 | UserStub model = new UserStub() 124 | { 125 | Age = 19, 126 | Name = "a", 127 | Surname = "b", 128 | Token = new TokenStub() { TokenNumber = "resul@gmail" } 129 | }; 130 | var validator = new UserStubStrongValidator(); 131 | 132 | bool actual = validator.IsValid(model); 133 | 134 | Assert.False(actual); 135 | } 136 | 137 | [Fact] 138 | public void Example10() 139 | { 140 | UserStub model = new UserStub() 141 | { 142 | Age = 20, 143 | Name = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 144 | Surname = "b", 145 | Token = new TokenStub() { TokenNumber = "resul@gmail.com" } 146 | }; 147 | var validator = new UserStubStrongValidator(); 148 | 149 | bool actual = validator.IsValid(model); 150 | 151 | Assert.False(actual); 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /DynamicVNET.Lib.Integration.Tests/RuleMarkerTest.cs: -------------------------------------------------------------------------------- 1 | using DynamicVNET.Lib.Exceptions; 2 | using Xunit; 3 | using DynamicVNET.Lib.Internal; 4 | using DynamicVNET.Lib.Core; 5 | using System.Linq.Expressions; 6 | using System; 7 | using System.Linq; 8 | using DynamicVNET.Lib.Integration.Tests.Stub; 9 | 10 | namespace DynamicVNET.Lib.Integration.Tests 11 | { 12 | /// 13 | /// 14 | /// 15 | public class RuleMarkerTest 16 | { 17 | [Fact] 18 | public void Null_GivenInvalidTypeMember_ThrowValidationMarkerException() 19 | { 20 | // Act 21 | UserStub model = new UserStub() 22 | { 23 | Name = "TestName", 24 | Surname = "TestSurname", 25 | Age = 30 26 | }; 27 | RuleMarker builder = new RuleMarker(); 28 | Expression> lambda = x => x.Age; 29 | IMember ageMember = new ExpressionMember(lambda, typeof(UserStub), typeof(int)); 30 | 31 | // Arrange 32 | // Assert 33 | Assert.Throws(() => { 34 | builder.Null(ageMember, ""); 35 | }); 36 | } 37 | 38 | [Fact] 39 | public void RulesCount_GivenTwoRules_ReturnsWillBeOne() 40 | { 41 | // Act 42 | int expectedCount = 1; 43 | RuleMarker builder = new RuleMarker(); 44 | builder.Required(x => x.Name) 45 | .Required(x => x.Name); 46 | 47 | // Arrange 48 | int actualCount = builder.Rules.Count(); 49 | 50 | // Assert 51 | Assert.Equal(expectedCount, actualCount); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /DynamicVNET.Lib.Integration.Tests/Stub/CustomModelValidator.cs: -------------------------------------------------------------------------------- 1 | namespace DynamicVNET.Lib.Integration.Tests.Stub 2 | { 3 | public class ModelValidator : BaseValidator 4 | { 5 | protected override void Configure(ITypeRuleMarker builder) 6 | { 7 | builder 8 | .For(x => x.Age) 9 | .Range(18, 22); 10 | 11 | builder 12 | .For(x => x.Surname) 13 | .Required() 14 | .MaxLen(4); 15 | 16 | builder 17 | .For(x => x.Token.TokenNumber) 18 | .EmailAddress(); 19 | 20 | builder 21 | .Branch(x => x.Age > 18, y => 22 | { 23 | y.StringLen(m => m.Name, 18); 24 | }); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /DynamicVNET.Lib.Integration.Tests/Stub/CustomStringValidator.cs: -------------------------------------------------------------------------------- 1 | namespace DynamicVNET.Lib.Integration.Tests.Stub 2 | { 3 | public class StringValidator : BaseValidator 4 | { 5 | protected override void Configure(ITypeRuleMarker builder) 6 | { 7 | builder 8 | .For(x => x) 9 | .Required() 10 | .StringLen(5); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /DynamicVNET.Lib.Integration.Tests/Stub/TokenStub.cs: -------------------------------------------------------------------------------- 1 | namespace DynamicVNET.Lib.Integration.Tests.Stub 2 | { 3 | public class TokenStub 4 | { 5 | public int? Version { get; set; } 6 | public TokenStub InnerToken { get; set; } 7 | public string TokenNumber { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /DynamicVNET.Lib.Integration.Tests/Stub/UserStub.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace DynamicVNET.Lib.Integration.Tests.Stub 4 | { 5 | public class UserStub 6 | { 7 | public int Age { get; set; } 8 | public string Name { get; set; } 9 | 10 | public string Surname { get; set; } 11 | 12 | public TokenStub Token { get; set; } 13 | 14 | public IEnumerable Tokens { get; set; } 15 | 16 | public int GetAge() 17 | { 18 | return this.Age; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DynamicVNET.Lib.Integration.Tests/Stub/UserStubST.cs: -------------------------------------------------------------------------------- 1 | namespace DynamicVNET.Lib.Integration.Tests.Stub 2 | { 3 | public struct UserStubST 4 | { 5 | public int Age { get; set; } 6 | public string Name { get; set; } 7 | 8 | public string Surname { get; set; } 9 | 10 | public TokenStub Token { get; set; } 11 | 12 | public UserStubST(string name, string surname, TokenStub token) 13 | { 14 | this.Age = 15; 15 | this.Name = name; 16 | this.Surname = surname; 17 | this.Token = token; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /DynamicVNET.Lib.Integration.Tests/Stub/UserStubStrongValidator.cs: -------------------------------------------------------------------------------- 1 | namespace DynamicVNET.Lib.Integration.Tests.Stub 2 | { 3 | public class UserStubStrongValidator : BaseValidator 4 | { 5 | public override bool FailFirst => true; 6 | 7 | protected override void Configure(ITypeRuleMarker builder) 8 | { 9 | builder 10 | .For(x => x.Age) 11 | .Range(18, 22); 12 | 13 | builder 14 | .For(x => x.Surname) 15 | .Required() 16 | .MaxLen(4); 17 | 18 | builder 19 | .For(x => x.Token.TokenNumber) 20 | .EmailAddress(); 21 | 22 | builder 23 | .Branch(x => x.Age > 18, y => 24 | { 25 | y.StringLen(m => m.Name, 18); 26 | }); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /DynamicVNET.Lib.Integration.Tests/ValidatorTest.cs: -------------------------------------------------------------------------------- 1 | using DynamicVNET.Lib.Integration.Tests.Stub; 2 | using Xunit; 3 | 4 | namespace DynamicVNET.Lib.Integration.Tests 5 | { 6 | public class ValidatorTest 7 | { 8 | [Fact] 9 | public void IsValid_WhenGivenInvalidObjectWithEmptyProperties_ReturnsFalse() 10 | { 11 | // Act 12 | UserStub userInstance = new UserStub(); 13 | var validator = ValidatorFactory.Create(builder => 14 | { 15 | builder 16 | .Required(x => x.Name) 17 | .Required(x => x.Surname); 18 | }); 19 | 20 | // Arrange 21 | bool isValid = validator.IsValid(userInstance); 22 | 23 | // Assert 24 | Assert.False(isValid); 25 | } 26 | 27 | [Fact] 28 | public void IsValid_WhenGivenInvalidObjectForGreaterThanMethod_ReturnsFalse() 29 | { 30 | // Act 31 | UserStub userInstance = new UserStub() 32 | { 33 | Age = 0 34 | }; 35 | var validator = ValidatorFactory.Create(builder => 36 | { 37 | var aa = builder; 38 | builder.GreaterThan(x => x.Age, 20); 39 | }); 40 | 41 | // Arrange 42 | bool isValid = validator.IsValid(userInstance); 43 | 44 | // Assert 45 | Assert.False(isValid); 46 | } 47 | 48 | [Fact] 49 | public void IsValid_WhenGivenInvalidArgumentForLessThanMethod_ReturnsFalse() 50 | { 51 | // Act 52 | UserStub userInstance = new UserStub() 53 | { 54 | Age = 21 55 | }; 56 | var validator = ValidatorFactory.Create(builder => 57 | { 58 | var aa = builder; 59 | builder.LessThan(x => x.Age, 20); 60 | }); 61 | 62 | // Arrange 63 | bool isValid = validator.IsValid(userInstance); 64 | 65 | // Assert 66 | Assert.False(isValid); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /DynamicVNET.Lib.Unit.Tests/DynamicVNET.Lib.Unit.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | false 9 | true 10 | 11 | DynamicVNET.Lib.Unit.Tests 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /DynamicVNET.Lib.Unit.Tests/ExpressionMemberTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using DynamicVNET.Lib.Internal; 4 | using Xunit; 5 | 6 | namespace DynamicVNET.Lib.Unit.Tests 7 | { 8 | /// 9 | /// 10 | /// 11 | public class ExpressionMemberTests 12 | { 13 | private UserStub _instance; 14 | 15 | /// 16 | /// Initializes a new instance of the class. 17 | /// 18 | public ExpressionMemberTests() 19 | { 20 | _instance = new UserStub 21 | { 22 | Age = 30, 23 | Token = new TokenStub 24 | { 25 | InnerToken = new TokenStub 26 | { 27 | TokenNumber = "11111" 28 | } 29 | }, 30 | }; 31 | } 32 | 33 | [Fact] 34 | public void Initialize_GivenNestedProperty_ReturnPropertyNameByModel() 35 | { 36 | // Act 37 | const string expected = nameof(_instance.Token.InnerToken.TokenNumber); 38 | Expression> lambda = (x) => x.Token.InnerToken.TokenNumber; 39 | 40 | // Arrange 41 | ExpressionMember vMember = new ExpressionMember(lambda, typeof(UserStub), typeof(string)); 42 | 43 | // Assert 44 | Assert.Equal(expected, vMember.Name); 45 | } 46 | 47 | [Fact] 48 | public void ResolveValue_GivenModelPropertyLambdaExpressionMethod_ReturnSameWithModelAge() 49 | { 50 | // Act 51 | const int expectedAge = 30; 52 | Expression> lambda = (model) => model.GetAge(); 53 | ExpressionMember vMember = new ExpressionMember(lambda, typeof(UserStub), typeof(int)); 54 | 55 | // Arrange 56 | object actualAge = vMember.ResolveValue(_instance); 57 | 58 | // Assert 59 | Assert.Equal(expectedAge, actualAge); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /DynamicVNET.Lib.Unit.Tests/RuleMarkerTests.cs: -------------------------------------------------------------------------------- 1 | using DynamicVNET.Lib.Exceptions; 2 | using Xunit; 3 | using DynamicVNET.Lib.Internal; 4 | using DynamicVNET.Lib.Core; 5 | using System.Linq.Expressions; 6 | using System; 7 | 8 | namespace DynamicVNET.Lib.Unit.Tests 9 | { 10 | /// 11 | /// 12 | /// 13 | public class RuleMarkerTests 14 | { 15 | [Fact] 16 | public void Null_WhenGivenInvalidTypeMember_ShouldThrowValidationMarkerException() 17 | { 18 | // Act 19 | UserStub model = new UserStub() 20 | { 21 | Name = "TestName", 22 | Surname = "TestSurname", 23 | Age = 30 24 | }; 25 | RuleMarker builder = new RuleMarker(); 26 | Expression> lambda = x => x.Age; 27 | IMember ageMember = new ExpressionMember(lambda, typeof(UserStub), typeof(int)); // must be mock 28 | 29 | // Arrange 30 | // Assert 31 | Assert.Throws(() => { 32 | builder.Null(ageMember, ""); 33 | }); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /DynamicVNET.Lib.Unit.Tests/Stub/TokenStub.cs: -------------------------------------------------------------------------------- 1 | namespace DynamicVNET.Lib.Unit.Tests 2 | { 3 | public class TokenStub 4 | { 5 | public TokenStub InnerToken { get; set; } 6 | public string TokenNumber { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /DynamicVNET.Lib.Unit.Tests/Stub/UserStub.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace DynamicVNET.Lib.Unit.Tests 4 | { 5 | public class UserStub 6 | { 7 | public int Age { get; set; } 8 | 9 | public string Name { get; set; } 10 | 11 | public string Surname { get; set; } 12 | 13 | public TokenStub Token { get; set; } 14 | 15 | public IEnumerable Tokens { get; set; } 16 | 17 | public int GetAge() 18 | { 19 | return this.Age; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DynamicVNET.Lib.Unit.Tests/Stub/UserStubST.cs: -------------------------------------------------------------------------------- 1 | namespace DynamicVNET.Lib.Unit.Tests 2 | { 3 | public struct UserStubST 4 | { 5 | public int Age { get; set; } 6 | 7 | public string Name { get; set; } 8 | 9 | public string Surname { get; set; } 10 | 11 | public TokenStub Token { get; set; } 12 | 13 | public UserStubST(string name, string surname, TokenStub token) 14 | { 15 | this.Age = 15; 16 | this.Name = name; 17 | this.Surname = surname; 18 | this.Token = token; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/Core/Exceptions/ValidationException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DynamicVNET.Lib.Exceptions 4 | { 5 | public class ValidationException : ApplicationException 6 | { 7 | /// 8 | /// Gets the name of the validation. 9 | /// 10 | /// 11 | /// The name of the validation. 12 | /// 13 | public string ValidationName { get; } 14 | 15 | /// 16 | /// Initializes a new instance of the class. 17 | /// 18 | /// Name of the validation. 19 | /// The message. 20 | /// The inner. 21 | public ValidationException(string validationName, string message, System.Exception inner) : base(message, inner) 22 | { 23 | this.ValidationName = validationName; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/Core/Exceptions/ValidationMarkerException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DynamicVNET.Lib.Exceptions 4 | { 5 | public class ValidationMarkerException : ApplicationException 6 | { 7 | /// 8 | /// Gets the name. 9 | /// 10 | /// 11 | /// The name. 12 | /// 13 | public string Name { get; } 14 | 15 | /// 16 | /// Initializes a new instance of the class. 17 | /// 18 | /// The name. 19 | /// The message. 20 | public ValidationMarkerException(string name,string message) : this(name,message,null) { } 21 | 22 | /// 23 | /// Initializes a new instance of the class. 24 | /// 25 | /// The name. 26 | /// The message. 27 | /// The inner. 28 | public ValidationMarkerException(string name,string message, Exception inner) : base(message, inner) 29 | { 30 | this.Name = name; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/Core/Internal/Applier/Applier.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using DynamicVNET.Lib.Internal; 4 | 5 | namespace DynamicVNET.Lib 6 | { 7 | public abstract class Applier 8 | { 9 | /// 10 | /// Apply to the specified instance. 11 | /// 12 | /// The instance. 13 | public IEnumerable Apply(IEnumerable validations, object instance) 14 | { 15 | if (validations == null) 16 | { 17 | throw new ArgumentNullException(nameof(validations)); 18 | } 19 | 20 | List appliedResults = new List(); 21 | 22 | foreach (var validationRule in validations) 23 | { 24 | ValidationRuleResult validationResult = validationRule.Validate(instance); 25 | 26 | if (IsSuitable(validationResult)) 27 | { 28 | ValidationRuleResult validationResultCopy = validationResult.Copy(); 29 | 30 | validationResultCopy = Override(validationResultCopy); 31 | 32 | if (validationResultCopy != null) 33 | { 34 | validationResult = validationResultCopy; 35 | } 36 | 37 | appliedResults.Add(validationResult); 38 | 39 | if (Break(validationResult)) 40 | { 41 | break; 42 | } 43 | } 44 | } 45 | 46 | return appliedResults.Count == 0 ? null : appliedResults; 47 | } 48 | 49 | /// The result. 50 | protected abstract bool IsSuitable(ValidationRuleResult result); 51 | 52 | /// The result. 53 | protected abstract bool Break(ValidationRuleResult result); 54 | 55 | /// 56 | protected abstract ValidationRuleResult Override(ValidationRuleResult result); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/Core/Internal/Applier/DefaultApplier.cs: -------------------------------------------------------------------------------- 1 | namespace DynamicVNET.Lib 2 | { 3 | /// 4 | public class DefaultApplier : Applier 5 | { 6 | /// 7 | protected override bool IsSuitable(ValidationRuleResult result) 8 | { 9 | if (result != null) 10 | { 11 | return true; 12 | } 13 | 14 | return false; 15 | } 16 | 17 | /// 18 | protected override bool Break(ValidationRuleResult result) 19 | { 20 | return false; 21 | } 22 | 23 | /// 24 | protected override ValidationRuleResult Override(ValidationRuleResult result) 25 | { 26 | return result; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/Core/Internal/Applier/FailFirstApplier.cs: -------------------------------------------------------------------------------- 1 | namespace DynamicVNET.Lib 2 | { 3 | /// 4 | public class FailFirstApplier : DefaultApplier 5 | { 6 | /// 7 | protected override bool Break(ValidationRuleResult result) 8 | { 9 | if (!result.IsValid) 10 | { 11 | return true; 12 | } 13 | 14 | return base.Break(result); 15 | } 16 | 17 | /// 18 | protected override bool IsSuitable(ValidationRuleResult result) 19 | { 20 | if (base.IsSuitable(result) && !result.IsValid) 21 | { 22 | return true; 23 | } 24 | 25 | return false; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/Core/Internal/Member/EmptyMember.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DynamicVNET.Lib.Internal 4 | { 5 | /// 6 | public class EmptyMember : IMember 7 | { 8 | /// 9 | public Type Type { get; } 10 | 11 | /// 12 | public bool IsNullable { get; } 13 | 14 | /// 15 | public bool IsFieldOrProperty { get; } 16 | 17 | /// 18 | public string Name { get; } 19 | 20 | /// 21 | public Defination TypeDefinedAs { get; } 22 | 23 | /// 24 | /// Initializes a new instance of the class. 25 | /// 26 | public EmptyMember(string name, 27 | Type type, 28 | bool isNullable = true, 29 | bool isFieldOrProperty = false, 30 | Defination typeDefinedAs = Defination.Primitive) 31 | { 32 | this.Name = name; 33 | this.Type = type; 34 | this.IsNullable = isNullable; 35 | this.IsFieldOrProperty = isFieldOrProperty; 36 | this.TypeDefinedAs = typeDefinedAs; 37 | } 38 | 39 | /// 40 | public object ResolveValue(object instance) 41 | { 42 | throw new NotImplementedException(); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/Core/Internal/Member/ExpressionMember.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | namespace DynamicVNET.Lib.Internal 5 | { 6 | /// 7 | public class ExpressionMember : IMember 8 | { 9 | private readonly Type _parentType; 10 | private readonly LambdaExpression _memberExpression; 11 | 12 | /// 13 | /// Initializes a new instance of the class. 14 | /// 15 | /// The expression. 16 | /// The parent. 17 | /// The member. 18 | /// 19 | /// member 20 | /// or 21 | /// expression 22 | /// 23 | internal ExpressionMember(LambdaExpression expression, 24 | Type parent, 25 | Type member) 26 | { 27 | this._parentType = parent; 28 | this.Type = member ?? throw new ArgumentNullException(nameof(member)); 29 | this._memberExpression = expression ?? throw new ArgumentNullException(nameof(expression)); 30 | 31 | this.Initialize(); 32 | } 33 | 34 | /// 35 | public bool IsNullable 36 | { 37 | get 38 | { 39 | if (Type == typeof(string) || Type.IsClass) 40 | { 41 | return true; 42 | } 43 | 44 | return Nullable.GetUnderlyingType(Type) != null; 45 | } 46 | } 47 | 48 | /// 49 | public string Name { get; private set; } 50 | 51 | /// 52 | public Type Type { get; } 53 | 54 | /// 55 | public bool IsFieldOrProperty { get; private set; } 56 | 57 | /// 58 | public Defination TypeDefinedAs { get; private set; } 59 | 60 | /// 61 | public object ResolveValue(object instance) 62 | { 63 | try 64 | { 65 | return _memberExpression 66 | .Compile() 67 | .DynamicInvoke(instance); 68 | } 69 | catch 70 | { 71 | throw; 72 | } 73 | } 74 | 75 | private void Initialize() 76 | { 77 | if (this._memberExpression.Body.NodeType != ExpressionType.Parameter) 78 | { 79 | this.TypeDefinedAs = GetDefinedAs(this.Type); 80 | 81 | if (IsDefinedAs(Defination.Class) || IsDefinedAs(Defination.Struct)) 82 | { 83 | TrySetDesriptorInfo(this._memberExpression); 84 | } 85 | } 86 | 87 | this.Name = string.IsNullOrEmpty(this.Name) ? "" : this.Name; 88 | } 89 | 90 | private bool TrySetDesriptorInfo(LambdaExpression memberExpression) 91 | { 92 | if (IsPropertyOrField(memberExpression.Body, out MemberExpression operand)) 93 | { 94 | if (operand.NodeType != ExpressionType.Parameter) 95 | { 96 | this.Name = operand.Member.Name; 97 | this.IsFieldOrProperty = true; 98 | return true; 99 | } 100 | } 101 | else if (IsMethod(memberExpression.Body, out MethodCallExpression callOperand)) 102 | { 103 | if (callOperand.NodeType != ExpressionType.Parameter) 104 | { 105 | this.Name = callOperand.Method.Name; 106 | this.IsFieldOrProperty = false; 107 | return true; 108 | } 109 | } 110 | 111 | return false; 112 | } 113 | 114 | private bool IsMethod(Expression toUp, out MethodCallExpression methodCallExpression) 115 | { 116 | if (toUp is MethodCallExpression) 117 | { 118 | methodCallExpression = toUp as MethodCallExpression; 119 | return true; 120 | } 121 | 122 | methodCallExpression = default(MethodCallExpression); 123 | return false; 124 | } 125 | 126 | private bool IsPropertyOrField(Expression toUp, out MemberExpression memberExpression) 127 | { 128 | if (toUp is UnaryExpression upOperand) 129 | { 130 | if ((memberExpression = (upOperand.Operand as MemberExpression)) != null) 131 | { 132 | return true; 133 | } 134 | } 135 | else if ((memberExpression = (toUp as MemberExpression)) != null) 136 | { 137 | return true; 138 | } 139 | 140 | memberExpression = default(MemberExpression); 141 | return false; 142 | } 143 | 144 | private bool IsDefinedAs(Defination defination) 145 | { 146 | if ((_parentType == typeof(string) && defination == Defination.String) 147 | || (_parentType.IsPrimitive && defination == Defination.Primitive) 148 | || (_parentType.IsValueType && defination == Defination.Struct) 149 | || (_parentType.IsClass && defination == Defination.Class)) 150 | { 151 | return true; 152 | } 153 | 154 | return false; 155 | } 156 | 157 | private Defination GetDefinedAs(Type memberType) 158 | { 159 | if (memberType == typeof(string)) 160 | return Defination.String; 161 | else if (memberType == typeof(double)) 162 | return Defination.Double; 163 | else if (memberType == typeof(decimal)) 164 | return Defination.Decimal; 165 | else if (memberType == typeof(float)) 166 | return Defination.Float; 167 | else if (memberType == typeof(int)) 168 | return Defination.Int; 169 | else if (memberType == typeof(byte)) 170 | return Defination.Byte; 171 | else if (memberType.IsPrimitive) 172 | return Defination.Primitive; 173 | else if (memberType.IsValueType) 174 | return Defination.Struct; 175 | else if (memberType.IsClass) 176 | return Defination.Class; 177 | else 178 | return Defination.Uknown; 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/Core/Internal/Member/ExpressionMemberFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Linq.Expressions; 4 | 5 | namespace DynamicVNET.Lib.Internal 6 | { 7 | /// 8 | /// Pooling factory for . 9 | /// Safely implemented through the ConcurrentDictionary. 10 | /// 11 | public static class ExpressionMemberFactory 12 | { 13 | private static ConcurrentDictionary _pool; 14 | 15 | /// 16 | /// Static initializer the class. 17 | /// 18 | static ExpressionMemberFactory() 19 | { 20 | _pool = new ConcurrentDictionary(); 21 | } 22 | 23 | /// 24 | /// Creates for instance. 25 | /// 26 | /// 27 | public static ExpressionMember Create() 28 | { 29 | Type type = typeof(T); 30 | Expression> expression = x => x; 31 | ExpressionMember newMember = new ExpressionMember(expression, type, type); 32 | return newMember; 33 | } 34 | 35 | /// 36 | /// Creates the specified expression. 37 | /// 38 | /// The expression. 39 | /// if set to true [in pool]. 40 | /// 41 | /// expression 42 | public static ExpressionMember Create(Expression> expression) 43 | { 44 | int _CreateHashCode(Type type, Expression expInstance) 45 | => $"{expInstance.ToString()}{type.FullName.ToString()}" 46 | .GetHashCode(); 47 | 48 | if (expression == null) 49 | { 50 | throw new ArgumentNullException(nameof(expression)); 51 | } 52 | 53 | int memberHashCode = _CreateHashCode(typeof(T), expression); 54 | 55 | if (_pool.TryGetValue(memberHashCode, out ExpressionMember member)) 56 | { 57 | return member; 58 | } 59 | else 60 | { 61 | var newMember = new ExpressionMember(expression, typeof(T), typeof(TMember)); 62 | _pool.TryAdd(memberHashCode, newMember); 63 | return newMember; 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/Core/Internal/Member/IMember.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DynamicVNET.Lib.Internal 4 | { 5 | public enum Defination : byte 6 | { 7 | Class = 1, 8 | Struct = 2, 9 | String = 3, 10 | Primitive = 4, 11 | Other = 5, 12 | Decimal = 6, 13 | Double = 7, 14 | Float = 8, 15 | Int = 9, 16 | Byte = 10, 17 | Uknown = 11, 18 | } 19 | 20 | public interface IMember 21 | { 22 | /// 23 | /// Gets the member type. 24 | /// 25 | Type Type { get; } 26 | 27 | /// 28 | /// Gets a value indicating whether member is nullable. 29 | /// 30 | bool IsNullable { get; } 31 | 32 | /// 33 | /// Gets a value indicating whether member is field or property. 34 | /// 35 | bool IsFieldOrProperty { get; } 36 | 37 | /// 38 | /// Gets the end of the name for example name of marked property or field. 39 | /// 40 | string Name { get; } 41 | 42 | /// 43 | /// Gets a definition of the type. 44 | /// 45 | Defination TypeDefinedAs { get; } 46 | 47 | /// 48 | /// Resolves the member value. 49 | /// 50 | /// The instance. 51 | object ResolveValue(object instance); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/Core/Internal/Rule/AnnotationRuleAdapter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | using DynamicVNET.Lib.Exceptions; 4 | 5 | namespace DynamicVNET.Lib.Internal 6 | { 7 | /// 8 | public class AnnotationRuleAdapter : BaseRule 9 | { 10 | private readonly ValidationAttribute _attribute; 11 | 12 | /// 13 | /// Initializes a new instance of the class. 14 | /// 15 | /// The validation. 16 | /// The context. 17 | /// 18 | /// OperationName 19 | /// or 20 | /// Member 21 | /// or 22 | /// ValidationAttribute 23 | /// 24 | public AnnotationRuleAdapter(ValidationAttribute validation, RuleContext context) : base(context) 25 | { 26 | if (string.IsNullOrEmpty(context.OperationName)) 27 | { 28 | throw new ArgumentNullException(nameof(context.OperationName)); 29 | } 30 | if (context.Member == null) 31 | { 32 | throw new ArgumentNullException(nameof(context.Member)); 33 | } 34 | 35 | this._attribute = validation ?? throw new ArgumentNullException(nameof(ValidationAttribute)); 36 | } 37 | 38 | /// 39 | /// Validate the specified instance. 40 | /// 41 | /// The instance. 42 | /// 43 | /// 44 | /// Occurred exception in marker (instance is null). Please check detail error information [InnerErorr]! 45 | /// or 46 | /// Occurred exception in [{nameof(AnnotationRuleAdapter)}]. Please check detail error information [InnerErorr]! 47 | /// 48 | public override ValidationRuleResult Validate(object instance) 49 | { 50 | try 51 | { 52 | var result = _attribute.GetValidationResult(Context.Member.ResolveValue(instance), 53 | new ValidationContext(instance)); 54 | 55 | if (result != null) 56 | { 57 | return ValidationRuleResult.Failure(this.Context.Member.Name, 58 | this.Context.OperationName, 59 | result.ErrorMessage); 60 | } 61 | 62 | return ValidationRuleResult.Success(this.Context.Member.Name, 63 | this.Context.OperationName); 64 | } 65 | catch (ArgumentNullException ex) 66 | { 67 | throw new ValidationMarkerException(Context.OperationName,"Occurred exception in marker (instance is null). Please check detail error information [InnerErorr]!", ex); 68 | } 69 | catch(Exception ex) 70 | { 71 | throw new ValidationMarkerException(Context.OperationName, $"Occurred exception in [{nameof(AnnotationRuleAdapter)}]. Please check detail error information [InnerErorr]!", ex); 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/Core/Internal/Rule/BaseRule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DynamicVNET.Lib.Internal 4 | { 5 | /// 6 | public abstract class BaseRule : IValidationRule 7 | { 8 | /// 9 | public RuleContext Context { get; } 10 | 11 | /// 12 | /// Initializes a new instance of the class. 13 | /// 14 | /// The context. 15 | /// RuleContext 16 | protected BaseRule(RuleContext context) 17 | { 18 | this.Context = context ?? throw new ArgumentNullException(nameof(RuleContext)); 19 | } 20 | 21 | /// 22 | public override bool Equals(object obj) 23 | { 24 | BaseRule rule = obj as BaseRule; 25 | 26 | if (rule == null) 27 | { 28 | return false; 29 | } 30 | else if (rule.GetHashCode() == this.GetHashCode()) 31 | { 32 | return true; 33 | } 34 | 35 | return false; 36 | } 37 | 38 | /// 39 | public override int GetHashCode() 40 | { 41 | return Context.GetHashCode(); 42 | } 43 | 44 | /// 45 | public abstract ValidationRuleResult Validate(object instance); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/Core/Internal/Rule/BranchRule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using DynamicVNET.Lib.Exceptions; 5 | 6 | namespace DynamicVNET.Lib.Internal 7 | { 8 | /// 9 | public class BranchRule : PredicateRule 10 | { 11 | private readonly IEnumerable _validationRules; 12 | 13 | /// 14 | /// 15 | /// 16 | /// 17 | /// 18 | /// 19 | /// 20 | /// 21 | public BranchRule(IEnumerable validationRules, Func predicate, RuleContext context, string errorMessage = null) : base(predicate, errorMessage, context) 22 | { 23 | _validationRules = validationRules ?? throw new ArgumentNullException(nameof(validationRules)); 24 | } 25 | 26 | /// 27 | public override ValidationRuleResult Validate(object instance) 28 | { 29 | try 30 | { 31 | if (base.Invoke(instance)) 32 | { 33 | var childResult = new FailFirstApplier().Apply(_validationRules, instance); 34 | 35 | if (childResult == null || !childResult.Any()) 36 | { 37 | return ValidationRuleResult.Success(this.Context.Member.Name, 38 | this.Context.OperationName); 39 | } 40 | 41 | return ValidationRuleResult.Failure(this.Context.Member.Name, 42 | this.Context.OperationName, 43 | this.ErrorMessage, 44 | childResult); 45 | } 46 | 47 | return null; 48 | } 49 | catch (Exception ex) 50 | { 51 | throw new ValidationMarkerException(nameof(BranchRule), $"Occurred exception in [{nameof(BranchRule)}]. Please check detail error information [InnerErorr]!", ex); 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/Core/Internal/Rule/Context/RuleContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DynamicVNET.Lib.Internal 4 | { 5 | /// 6 | /// 7 | /// 8 | public class RuleContext 9 | { 10 | /// 11 | /// Gets the name of the operation. 12 | /// 13 | /// 14 | /// The name of the operation. 15 | /// 16 | public string OperationName { get; } 17 | 18 | /// 19 | /// Gets the member. 20 | /// 21 | /// 22 | /// The member. 23 | /// 24 | public IMember Member { get; } 25 | 26 | /// 27 | /// Initializes a new instance of the class. 28 | /// 29 | /// Name of the operation. 30 | /// The member. 31 | /// operationName 32 | public RuleContext(string operationName, IMember member) 33 | { 34 | if (string.IsNullOrEmpty(operationName)) 35 | { 36 | throw new ArgumentNullException(nameof(operationName)); 37 | } 38 | 39 | this.OperationName = operationName; 40 | this.Member = member; 41 | } 42 | 43 | /// 44 | public override int GetHashCode() 45 | { 46 | return $"{OperationName}{this.Member?.Name}".GetHashCode(); 47 | } 48 | 49 | /// 50 | /// Creates the copy. 51 | /// 52 | /// Name of the operation. 53 | public RuleContext CreateCopy(string operationName) 54 | { 55 | return new RuleContext(operationName, this.Member); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/Core/Internal/Rule/IValidation.cs: -------------------------------------------------------------------------------- 1 | namespace DynamicVNET.Lib.Internal 2 | { 3 | public interface IValidation 4 | { 5 | /// 6 | /// Validate the specified instance. 7 | /// 8 | /// The instance. 9 | ValidationRuleResult Validate(object instance); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/Core/Internal/Rule/IValidationRule.cs: -------------------------------------------------------------------------------- 1 | namespace DynamicVNET.Lib.Internal 2 | { 3 | public interface IValidationRule : IValidation 4 | { 5 | /// 6 | /// Gets the context. 7 | /// 8 | /// 9 | /// The context. 10 | /// 11 | RuleContext Context { get; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/Core/Internal/Rule/PredicateRule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using DynamicVNET.Lib.Exceptions; 3 | 4 | namespace DynamicVNET.Lib.Internal 5 | { 6 | /// 7 | public class PredicateRule : IValidationRule 8 | { 9 | /// 10 | /// The predicate 11 | /// 12 | private readonly Func _predicate; 13 | 14 | /// 15 | /// The error message 16 | /// 17 | protected string ErrorMessage; 18 | 19 | /// 20 | /// Gets the context. 21 | /// 22 | /// 23 | /// The context. 24 | /// 25 | public RuleContext Context { get; } 26 | 27 | /// 28 | /// Initializes a new instance of the class. 29 | /// 30 | /// The predicate. 31 | /// The error message. 32 | /// The context. 33 | /// predicate 34 | public PredicateRule(Func predicate, string errorMessage, RuleContext context) 35 | { 36 | this._predicate = predicate ?? throw new ArgumentNullException(nameof(predicate)); 37 | this.ErrorMessage = errorMessage; 38 | this.Context = context; 39 | } 40 | 41 | /// 42 | public virtual ValidationRuleResult Validate(object instance) 43 | { 44 | try 45 | { 46 | if (!Invoke(instance)) 47 | { 48 | return ValidationRuleResult.Failure(this.Context.Member.Name, 49 | this.Context.OperationName, 50 | this.ErrorMessage); 51 | } 52 | 53 | return ValidationRuleResult.Success(this.Context.Member.Name, 54 | this.Context.OperationName); 55 | } 56 | catch (Exception ex) 57 | { 58 | throw new ValidationMarkerException(nameof(PredicateRule), $"Occurred exception in [{nameof(PredicateRule)}]. Please check detail error information [InnerErorr]!", ex); 59 | } 60 | } 61 | 62 | /// 63 | /// Invoke predicate with specified instance. 64 | /// 65 | /// The instance. 66 | protected bool Invoke(object instance) 67 | { 68 | return _predicate((T)instance); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/Core/Internal/Rule/UniquePredicateRule.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace DynamicVNET.Lib.Internal 3 | { 4 | /// 5 | public class UniquePredicateRule : BaseRule 6 | { 7 | private readonly PredicateRule _predicate; 8 | 9 | /// 10 | /// Initializes a new instance of the class. 11 | /// 12 | /// The predicate rule. 13 | /// The context. 14 | public UniquePredicateRule(PredicateRule predicateRule, RuleContext context) : base(context) 15 | { 16 | _predicate = predicateRule; 17 | } 18 | 19 | /// 20 | public override ValidationRuleResult Validate(object instance) 21 | { 22 | return this._predicate.Validate(instance); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/Core/Internal/RuleCollection.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace DynamicVNET.Lib.Internal 6 | { 7 | /// 8 | /// Implemented for escaping concurrency loop problem. 9 | /// 10 | internal class RuleCollection : IEnumerable, ICollection 11 | { 12 | private ICollection _markers; 13 | 14 | /// 15 | /// Initializes a new instance of the class. 16 | /// 17 | public RuleCollection() 18 | { 19 | _markers = new HashSet(); 20 | } 21 | 22 | /// 23 | /// Initializes a new instance of the class. 24 | /// 25 | /// The markers. 26 | public RuleCollection(IEnumerable markers) 27 | { 28 | _markers = new HashSet(markers); 29 | } 30 | 31 | /// 32 | /// Gets the number of elements contained in the . 33 | /// 34 | public int Count => this._markers.Count; 35 | 36 | /// 37 | /// Gets a value indicating whether the is read-only. 38 | /// 39 | public bool IsReadOnly => this._markers.IsReadOnly; 40 | 41 | /// 42 | /// Adds an item to the . 43 | /// 44 | /// The object to add to the . 45 | public void Add(IValidationRule item) 46 | { 47 | _markers.Add(item); 48 | } 49 | 50 | /// 51 | /// Removes all items from the . 52 | /// 53 | public void Clear() 54 | { 55 | this._markers.Clear(); 56 | } 57 | 58 | /// 59 | /// Determines whether this instance contains the object. 60 | /// 61 | /// The object to locate in the . 62 | /// 63 | /// true if item is found in the ; otherwise, false. 64 | /// 65 | public bool Contains(IValidationRule item) 66 | { 67 | return this._markers.Contains(item); 68 | } 69 | 70 | /// 71 | /// Copies the elements of the to an , starting at a particular index. 72 | /// 73 | /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. 74 | /// The zero-based index in array at which copying begins. 75 | public void CopyTo(IValidationRule[] array, int arrayIndex) 76 | { 77 | this._markers.CopyTo(array, arrayIndex); 78 | } 79 | 80 | /// 81 | /// Returns an enumerator that iterates through the collection. 82 | /// 83 | /// 84 | /// An enumerator that can be used to iterate through the collection. 85 | /// 86 | public IEnumerator GetEnumerator() 87 | { 88 | return new RuleEnumerator(this); 89 | } 90 | 91 | 92 | /// 93 | /// Removes the first occurrence of a specific object from the . 94 | /// 95 | /// The object to remove from the . 96 | /// 97 | /// true if item was successfully removed from the ; otherwise, false. This method also returns false if item is not found in the original . 98 | /// 99 | public bool Remove(IValidationRule item) 100 | { 101 | return this._markers.Remove(item); 102 | } 103 | 104 | /// 105 | /// Returns an enumerator that iterates through a collection. 106 | /// 107 | /// 108 | /// An object that can be used to iterate through the collection. 109 | /// 110 | IEnumerator IEnumerable.GetEnumerator() 111 | { 112 | return new RuleEnumerator(this); 113 | } 114 | 115 | struct RuleEnumerator : IEnumerator 116 | { 117 | private RuleCollection _root; 118 | private IValidationRule _current; 119 | private int _currentIndex; 120 | public IValidationRule Current 121 | { 122 | get 123 | { 124 | return _current; 125 | } 126 | } 127 | 128 | object IEnumerator.Current 129 | { 130 | get 131 | { 132 | return _current; 133 | } 134 | } 135 | 136 | internal RuleEnumerator(RuleCollection rootCollection) 137 | { 138 | _currentIndex = 0; 139 | _current = null; 140 | _root = rootCollection; 141 | } 142 | 143 | public void Dispose() 144 | { 145 | Reset(); 146 | } 147 | 148 | public bool MoveNext() 149 | { 150 | if (_currentIndex >= _root.Count) 151 | return false; 152 | else 153 | { 154 | _current = _root._markers.ElementAt(_currentIndex); 155 | _currentIndex += 1; 156 | } 157 | return true; 158 | } 159 | 160 | public void Reset() 161 | { 162 | _currentIndex = 0; 163 | } 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/Core/Internal/ValidationRuleResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using DynamicVNET.Lib.Exceptions; 5 | 6 | namespace DynamicVNET.Lib 7 | { 8 | /// 9 | /// Defines a validation result 10 | /// 11 | public class ValidationRuleResult 12 | { 13 | /// 14 | /// Gets the name of the member. 15 | /// 16 | /// 17 | /// The name of the member. 18 | /// 19 | public string MemberName { get; } 20 | 21 | /// 22 | /// Returns true if ... is valid. 23 | /// 24 | /// 25 | /// true if this instance is valid; otherwise, false. 26 | /// 27 | public bool IsValid { get; } 28 | 29 | /// 30 | /// Gets the nested results. 31 | /// 32 | /// 33 | /// The nested results. 34 | /// 35 | public IEnumerable NestedResults { get; internal set; } 36 | 37 | /// 38 | /// Gets the name of the validation. 39 | /// 40 | /// 41 | /// The name of the validation. 42 | /// 43 | public string ValidationName { get; } 44 | 45 | /// 46 | /// Gets the error message. 47 | /// 48 | /// 49 | /// The error message. 50 | /// 51 | public string ErrorMessage => this.ErrorInfo?.Message; 52 | 53 | /// 54 | /// Gets the error information. 55 | /// 56 | /// 57 | /// The error information. 58 | /// 59 | public ValidationException ErrorInfo { get; } 60 | 61 | /// 62 | /// Gets a value indicating whether [exists nested results]. 63 | /// 64 | /// 65 | /// true if [exists nested results]; otherwise, false. 66 | /// 67 | public bool HasNestedResults => NestedResults != null && NestedResults.Any() ? true : false; 68 | 69 | /// 70 | /// Initializes a new instance of the class. 71 | /// 72 | /// if set to true [validate state]. 73 | /// Name of the member. 74 | /// Name of the validation. 75 | /// The error. 76 | /// The results. 77 | /// validationName 78 | internal ValidationRuleResult(bool validateState, string memberName,string validationName, ValidationException error, IEnumerable results) 79 | { 80 | this.IsValid = validateState; 81 | this.MemberName = memberName; 82 | this.ValidationName = validationName ?? throw new ArgumentNullException(nameof(validationName)); 83 | this.ErrorInfo = error; 84 | this.NestedResults = results; 85 | } 86 | 87 | /// 88 | /// 89 | /// 90 | public ValidationRuleResult Copy() 91 | { 92 | return new ValidationRuleResult(this.IsValid, this.MemberName, this.ValidationName, this.ErrorInfo, this.NestedResults); 93 | } 94 | 95 | /// 96 | /// Successes the specified member name. 97 | /// 98 | /// Name of the member. 99 | /// Name of the validation. 100 | /// 101 | public static ValidationRuleResult Success(string memberName,string validationName) 102 | { 103 | return new ValidationRuleResult(true, memberName, validationName, null, null); 104 | } 105 | 106 | /// 107 | /// Failures the specified member name. 108 | /// 109 | /// Name of the member. 110 | /// Name of the validation. 111 | /// The error message. 112 | /// 113 | public static ValidationRuleResult Failure(string memberName, string validationName, string errorMessage) 114 | { 115 | return new ValidationRuleResult(false, memberName, validationName, new ValidationException(validationName, errorMessage, null), null); 116 | } 117 | 118 | /// 119 | /// Failures the specified member name. 120 | /// 121 | /// Name of the member. 122 | /// Name of the validation. 123 | /// The error message. 124 | /// The results. 125 | /// 126 | public static ValidationRuleResult Failure(string memberName, string validationName, string errorMessage, IEnumerable results) 127 | { 128 | return new ValidationRuleResult(false, memberName, validationName, new ValidationException(validationName, errorMessage, null), results); 129 | } 130 | 131 | /// 132 | /// Failures the specified member name. 133 | /// 134 | /// Name of the member. 135 | /// Name of the validation. 136 | /// The error information. 137 | /// The results. 138 | /// 139 | public static ValidationRuleResult Failure(string memberName, string validationName, ValidationException errorInfo, IEnumerable results) 140 | { 141 | return new ValidationRuleResult(false, memberName, validationName, errorInfo, results); 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/Core/Marker/BaseRuleMarker.cs: -------------------------------------------------------------------------------- 1 | using DynamicVNET.Lib.Internal; 2 | using System.Collections.Generic; 3 | 4 | namespace DynamicVNET.Lib 5 | { 6 | public abstract class BaseRuleMarker : IRuleMarker 7 | { 8 | private ICollection _rules; 9 | 10 | /// 11 | public IEnumerable Rules => this._rules; 12 | 13 | /// 14 | /// Initializes a new instance of the class. 15 | /// 16 | protected BaseRuleMarker() 17 | { 18 | this._rules = new RuleCollection(); 19 | } 20 | 21 | /// 22 | public void Add(IValidationRule rule) 23 | { 24 | if (rule == null) 25 | { 26 | throw new System.ArgumentNullException(nameof(rule)); 27 | } 28 | 29 | this._rules.Add(rule); 30 | } 31 | 32 | /// 33 | public void Add(IEnumerable rules) 34 | { 35 | if (rules == null) 36 | { 37 | throw new System.ArgumentNullException(nameof(rules)); 38 | } 39 | 40 | foreach (var item in rules) 41 | { 42 | this.Add(item); 43 | } 44 | } 45 | 46 | /// 47 | /// Set the specified rules. 48 | /// 49 | /// The rules. 50 | protected void Set(IEnumerable rules) 51 | { 52 | _rules = new RuleCollection(rules); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/Core/Marker/IMemberRuleMarker.cs: -------------------------------------------------------------------------------- 1 | using DynamicVNET.Lib.Internal; 2 | 3 | namespace DynamicVNET.Lib 4 | { 5 | public interface IMemberRuleMarker : IRuleMarker 6 | { 7 | /// 8 | /// Get and Set the selected. 9 | /// 10 | IMember Selected { get; set; } 11 | 12 | /// 13 | /// Create the copy. 14 | /// 15 | IMemberRuleMarker Copy(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/Core/Marker/IRuleMarker.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using DynamicVNET.Lib.Internal; 3 | 4 | namespace DynamicVNET.Lib 5 | { 6 | public interface IRuleMarker 7 | { 8 | /// 9 | /// Get the rules. 10 | /// 11 | /// 12 | /// The rules. 13 | /// 14 | IEnumerable Rules { get; } 15 | 16 | /// 17 | /// Add the specified rule. 18 | /// 19 | /// The rule. 20 | void Add(IValidationRule rule); 21 | 22 | /// 23 | /// Add the specified rules. 24 | /// 25 | /// The rules. 26 | void Add(IEnumerable rules); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/Core/Marker/ITypeRuleMarker.cs: -------------------------------------------------------------------------------- 1 | namespace DynamicVNET.Lib 2 | { 3 | /// 4 | /// IH markable interface 5 | /// 6 | public interface ITypeRuleMarker : IRuleMarker 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/Core/Marker/RuleMarker.cs: -------------------------------------------------------------------------------- 1 | using DynamicVNET.Lib.Internal; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace DynamicVNET.Lib 6 | { 7 | /// 8 | public class RuleMarker : BaseRuleMarker, ITypeRuleMarker, IMemberRuleMarker 9 | { 10 | private IMember _selected; 11 | 12 | /// 13 | /// Initializes a new instance of the class. 14 | /// 15 | public RuleMarker() : base() { } 16 | 17 | /// 18 | /// Initializes a new instance of the class. 19 | /// 20 | /// The rules. 21 | public RuleMarker(IEnumerable rules) 22 | { 23 | if (rules != null) 24 | { 25 | Set(rules); 26 | } 27 | } 28 | 29 | /// 30 | /// Initializes a new instance of the class. 31 | /// 32 | /// The member. 33 | /// IMember 34 | internal RuleMarker(IMember member) 35 | { 36 | Selected = member ?? throw new ArgumentNullException(nameof(member)); 37 | } 38 | 39 | /// 40 | public IMember Selected 41 | { 42 | get 43 | { 44 | return _selected == null ? 45 | throw new NullReferenceException(nameof(IMember)) : _selected; 46 | } 47 | set 48 | { 49 | _selected = value; 50 | } 51 | } 52 | 53 | /// 54 | public IMemberRuleMarker Copy() 55 | { 56 | return new RuleMarker(this.Selected); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/Core/RuleExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | using DynamicVNET.Lib.Exceptions; 4 | using DynamicVNET.Lib.Internal; 5 | 6 | namespace DynamicVNET.Lib.Core 7 | { 8 | public static class RuleExtensions 9 | { 10 | // For regular expressions. 11 | public const string URL_PATTERN = @"(mailto\:|(news|(ht|f)tp(s?))\://)(([^[:space:]]+)|([^[:space:]]+)( #([^#]+)#)?)"; 12 | public const string EMAIL_PATTERN = @"^([0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*@([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9})$"; 13 | 14 | // For errors. 15 | public const string ERROR_NULL = "Member is not null, validation invalid!"; 16 | public const string ERROR_NOT_NULL = "Member is null, validation invalid!"; 17 | public const string ERROR_CUSTOM = "Custom condition fail, validation invalid!"; 18 | 19 | public static IRuleMarker Null(this IRuleMarker builder, IMember member, string errorMessage) 20 | { 21 | if (!member.IsNullable) 22 | { 23 | throw new ValidationMarkerException(nameof(Null), $"This method can not be apply to {member.Type.ToString()}!"); 24 | } 25 | 26 | var context = new RuleContext(nameof(Null), member); 27 | builder.Add(new UniquePredicateRule(new PredicateRule(instance => 28 | { 29 | if (member.ResolveValue(instance) == null) 30 | { 31 | return true; 32 | } 33 | 34 | return false; 35 | }, errorMessage, context), context)); 36 | return builder; 37 | } 38 | 39 | public static IRuleMarker NotNull(this IRuleMarker builder, IMember member, string errorMessage) 40 | { 41 | if (!member.IsNullable) 42 | { 43 | throw new ValidationMarkerException(nameof(NotNull), $"This method can not be apply to {member.Type.ToString()} type!"); 44 | } 45 | 46 | var context = new RuleContext(nameof(NotNull), member); 47 | builder.Add(new UniquePredicateRule(new PredicateRule(instance => 48 | { 49 | if (member.ResolveValue(instance) != null) 50 | { 51 | return true; 52 | } 53 | 54 | return false; 55 | }, errorMessage, context), context)); 56 | return builder; 57 | } 58 | 59 | public static IRuleMarker StringLen(this IRuleMarker builder, IMember member, int max, int? min = null) 60 | { 61 | StringLengthAttribute strLenAnnotation = new StringLengthAttribute(max); 62 | 63 | if (min != null) 64 | { 65 | strLenAnnotation.MinimumLength = min.Value; 66 | } 67 | 68 | builder.Add(new AnnotationRuleAdapter(strLenAnnotation, 69 | new RuleContext(nameof(StringLen), member))); 70 | return builder; 71 | } 72 | 73 | public static IRuleMarker RegularExp(this IRuleMarker builder, IMember member, string operationName ,string pattern) 74 | { 75 | builder.Add(new AnnotationRuleAdapter(new RegularExpressionAttribute(pattern), 76 | new RuleContext(operationName, member))); 77 | return builder; 78 | } 79 | 80 | public static IRuleMarker MaxLen(this IRuleMarker builder, IMember member, int length) 81 | { 82 | builder.Add(new AnnotationRuleAdapter(new MaxLengthAttribute(length), 83 | new RuleContext(nameof(MaxLen), member))); 84 | return builder; 85 | } 86 | 87 | public static IRuleMarker Required(this IRuleMarker builder, IMember member) 88 | { 89 | builder.Add(new AnnotationRuleAdapter(new RequiredAttribute(), 90 | new RuleContext(nameof(Required), member))); 91 | return builder; 92 | } 93 | 94 | public static IRuleMarker Range(this IRuleMarker builder, IMember member, int min, int max) 95 | { 96 | builder.Add(new AnnotationRuleAdapter(new RangeAttribute(min, max), 97 | new RuleContext(nameof(Range), member))); 98 | return builder; 99 | } 100 | 101 | public static IRuleMarker Range(this IRuleMarker builder, IMember member, double min, double max) 102 | { 103 | builder.Add(new AnnotationRuleAdapter(new RangeAttribute(min, max), 104 | new RuleContext(nameof(Range), member))); 105 | return builder; 106 | } 107 | 108 | public static IRuleMarker Range(this IRuleMarker builder, IMember member, Type type, string min, string max) 109 | { 110 | builder.Add(new AnnotationRuleAdapter(new RangeAttribute(type, min, max), 111 | new RuleContext(nameof(Range), member))); 112 | return builder; 113 | } 114 | 115 | public static IRuleMarker EmailAddress(this IRuleMarker builder, IMember member) 116 | { 117 | return builder.RegularExp(member, nameof(EmailAddress) ,EMAIL_PATTERN); 118 | } 119 | 120 | public static IRuleMarker Url(this IRuleMarker builder, IMember member) 121 | { 122 | return builder.RegularExp(member, nameof(Url), URL_PATTERN); 123 | } 124 | 125 | public static IRuleMarker GreaterThan(this IRuleMarker builder, IMember member, int minValue, string errorMessage) 126 | { 127 | if (member.TypeDefinedAs != Defination.Int) 128 | { 129 | throw new ValidationMarkerException(nameof(GreaterThan), $"This method can not be apply to {member.Type.ToString()}!"); 130 | } 131 | 132 | RuleContext context = new RuleContext(nameof(GreaterThan), member); 133 | 134 | builder.Add(new UniquePredicateRule(new PredicateRule(instance => 135 | { 136 | int valueOfValidationInstance = int.Parse(member.ResolveValue(instance).ToString()); 137 | 138 | if (valueOfValidationInstance > minValue) 139 | { 140 | return true; 141 | } 142 | 143 | return false; 144 | }, errorMessage, context), context)); 145 | return builder; 146 | } 147 | 148 | public static IRuleMarker LessThan(this IRuleMarker builder, IMember member, int maxValue, string errorMessage) 149 | { 150 | if (member.TypeDefinedAs != Defination.Int) 151 | { 152 | throw new ValidationMarkerException(nameof(LessThan), $"This method can not be apply to {member.Type.ToString()}!"); 153 | } 154 | 155 | RuleContext context = new RuleContext(nameof(LessThan), member); 156 | 157 | builder.Add(new UniquePredicateRule(new PredicateRule(instance => 158 | { 159 | int valueOfValidationInstance = int.Parse(member.ResolveValue(instance).ToString()); 160 | 161 | if (valueOfValidationInstance < maxValue) 162 | { 163 | return true; 164 | } 165 | 166 | return false; 167 | }, errorMessage, context), context)); 168 | return builder; 169 | } 170 | 171 | public static IRuleMarker GreaterThan(this IRuleMarker builder, IMember member, double minValue, string errorMessage) 172 | { 173 | if (member.TypeDefinedAs != Defination.Double) 174 | { 175 | throw new ValidationMarkerException(nameof(GreaterThan), $"This method can not be apply to {member.Type.ToString()}!"); 176 | } 177 | 178 | RuleContext context = new RuleContext(nameof(GreaterThan), member); 179 | 180 | builder.Add(new UniquePredicateRule(new PredicateRule(instance => 181 | { 182 | double valueOfValidationInstance = double.Parse(member.ResolveValue(instance).ToString()); 183 | 184 | if (valueOfValidationInstance > minValue) 185 | { 186 | return true; 187 | } 188 | 189 | return false; 190 | }, errorMessage, context), context)); 191 | return builder; 192 | } 193 | 194 | public static IRuleMarker LessThan(this IRuleMarker builder, IMember member, double maxValue, string errorMessage) 195 | { 196 | if (member.TypeDefinedAs != Defination.Double) 197 | { 198 | throw new ValidationMarkerException(nameof(LessThan), $"This method can not be apply to {member.Type.ToString()}!"); 199 | } 200 | 201 | RuleContext context = new RuleContext(nameof(LessThan), member); 202 | 203 | builder.Add(new UniquePredicateRule(new PredicateRule(instance => 204 | { 205 | double valueOfValidationInstance = double.Parse(member.ResolveValue(instance).ToString()); 206 | 207 | if (valueOfValidationInstance < maxValue) 208 | { 209 | return true; 210 | } 211 | 212 | return false; 213 | }, errorMessage, context), context)); 214 | return builder; 215 | } 216 | 217 | public static IRuleMarker GreaterThan(this IRuleMarker builder, IMember member, decimal minValue, string errorMessage) 218 | { 219 | if (member.TypeDefinedAs != Defination.Decimal) 220 | { 221 | throw new ValidationMarkerException(nameof(GreaterThan), $"This method can not be apply to {member.Type.ToString()}!"); 222 | } 223 | 224 | RuleContext context = new RuleContext(nameof(GreaterThan), member); 225 | 226 | builder.Add(new UniquePredicateRule(new PredicateRule(instance => 227 | { 228 | decimal valueOfValidationInstance = decimal.Parse(member.ResolveValue(instance).ToString()); 229 | 230 | if (valueOfValidationInstance > minValue) 231 | { 232 | return true; 233 | } 234 | 235 | return false; 236 | }, errorMessage, context), context)); 237 | return builder; 238 | } 239 | 240 | public static IRuleMarker LessThan(this IRuleMarker builder, IMember member, decimal maxValue, string errorMessage) 241 | { 242 | if (member.TypeDefinedAs != Defination.Decimal) 243 | { 244 | throw new ValidationMarkerException(nameof(LessThan), $"This method can not be apply to {member.Type.ToString()}!"); 245 | } 246 | 247 | RuleContext context = new RuleContext(nameof(LessThan), member); 248 | 249 | builder.Add(new UniquePredicateRule(new PredicateRule(instance => 250 | { 251 | decimal valueOfValidationInstance = decimal.Parse(member.ResolveValue(instance).ToString()); 252 | 253 | if (valueOfValidationInstance < maxValue) 254 | { 255 | return true; 256 | } 257 | 258 | return false; 259 | }, errorMessage, context), context)); 260 | return builder; 261 | } 262 | 263 | public static IRuleMarker GreaterThan(this IRuleMarker builder, IMember member, float minValue, string errorMessage) 264 | { 265 | if (member.TypeDefinedAs != Defination.Float) 266 | { 267 | throw new ValidationMarkerException(nameof(GreaterThan), $"This method can not be apply to {member.Type.ToString()}!"); 268 | } 269 | 270 | RuleContext context = new RuleContext(nameof(GreaterThan), member); 271 | 272 | builder.Add(new UniquePredicateRule(new PredicateRule(instance => 273 | { 274 | float valueOfValidationInstance = float.Parse(member.ResolveValue(instance).ToString()); 275 | 276 | if (valueOfValidationInstance > minValue) 277 | { 278 | return true; 279 | } 280 | 281 | return false; 282 | }, errorMessage, context), context)); 283 | return builder; 284 | } 285 | 286 | public static IRuleMarker LessThan(this IRuleMarker builder, IMember member, float maxValue, string errorMessage) 287 | { 288 | if (member.TypeDefinedAs != Defination.Float) 289 | { 290 | throw new ValidationMarkerException(nameof(LessThan), $"This method can not be apply to {member.Type.ToString()}!"); 291 | } 292 | 293 | RuleContext context = new RuleContext(nameof(LessThan), member); 294 | 295 | builder.Add(new UniquePredicateRule(new PredicateRule(instance => 296 | { 297 | float valueOfValidationInstance = float.Parse(member.ResolveValue(instance).ToString()); 298 | 299 | if (valueOfValidationInstance < maxValue) 300 | { 301 | return true; 302 | } 303 | 304 | return false; 305 | }, errorMessage, context), context)); 306 | return builder; 307 | } 308 | 309 | public static IRuleMarker GreaterThan(this IRuleMarker builder, IMember member, byte minValue, string errorMessage) 310 | { 311 | if (member.TypeDefinedAs != Defination.Byte) 312 | { 313 | throw new ValidationMarkerException(nameof(GreaterThan), $"This method can not be apply to {member.Type.ToString()}!"); 314 | } 315 | 316 | RuleContext context = new RuleContext(nameof(GreaterThan), member); 317 | 318 | builder.Add(new UniquePredicateRule(new PredicateRule(instance => 319 | { 320 | byte valueOfValidationInstance = byte.Parse(member.ResolveValue(instance).ToString()); 321 | 322 | if (valueOfValidationInstance > minValue) 323 | { 324 | return true; 325 | } 326 | 327 | return false; 328 | }, errorMessage, context), context)); 329 | return builder; 330 | } 331 | 332 | public static IRuleMarker LessThan(this IRuleMarker builder, IMember member, byte maxValue, string errorMessage) 333 | { 334 | if (member.TypeDefinedAs != Defination.Byte) 335 | { 336 | throw new ValidationMarkerException(nameof(LessThan), $"This method can not be apply to {member.Type.ToString()}!"); 337 | } 338 | 339 | RuleContext context = new RuleContext(nameof(LessThan), member); 340 | 341 | builder.Add(new UniquePredicateRule(new PredicateRule(instance => 342 | { 343 | byte valueOfValidationInstance = byte.Parse(member.ResolveValue(instance).ToString()); 344 | 345 | if (valueOfValidationInstance < maxValue) 346 | { 347 | return true; 348 | } 349 | 350 | return false; 351 | }, errorMessage, context), context)); 352 | return builder; 353 | } 354 | } 355 | } 356 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/Core/Validator/BaseValidator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace DynamicVNET.Lib 5 | { 6 | public abstract class BaseValidator : IValidator 7 | { 8 | private readonly IValidator _validator; 9 | 10 | public Type ValidateType => typeof(T); 11 | 12 | /// 13 | /// Allows you to abort a validation, if at least one validation fails. 14 | /// 15 | public virtual bool FailFirst { get; } = false; 16 | 17 | protected BaseValidator() 18 | { 19 | _validator = ValidatorFactory.Create(Configure, FailFirst); 20 | } 21 | 22 | /// 23 | public bool IsValid(T instance) 24 | { 25 | return _validator.IsValid(instance); 26 | } 27 | 28 | /// 29 | public bool IsValid(object instance) 30 | { 31 | return _validator.IsValid(instance); 32 | } 33 | 34 | /// 35 | public IEnumerable Validate(T instance) 36 | { 37 | return _validator.Validate(instance); 38 | } 39 | 40 | /// 41 | public IEnumerable Validate(object instance) 42 | { 43 | return _validator.Validate(instance); 44 | } 45 | 46 | /// 47 | /// 48 | /// 49 | protected abstract void Configure(ITypeRuleMarker builder); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/Core/Validator/Common/IValidator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace DynamicVNET.Lib 5 | { 6 | /// 7 | /// 8 | /// 9 | public interface IValidator 10 | { 11 | /// 12 | /// The type of the validated by Validator 13 | /// 14 | Type ValidateType { get; } 15 | 16 | /// 17 | /// Returns true if ... is valid. 18 | /// 19 | /// The instance. 20 | /// 21 | /// true if the specified instance is valid; otherwise, false. 22 | /// 23 | bool IsValid(object instance); 24 | 25 | /// 26 | /// Validate the specified instance. 27 | /// 28 | /// The instance. 29 | /// 30 | IEnumerable Validate(object instance); 31 | } 32 | 33 | /// 34 | /// 35 | public interface IValidator : IValidator 36 | { 37 | /// 38 | bool IsValid(T instance); 39 | 40 | /// 41 | IEnumerable Validate(T instance); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/Core/Validator/Common/MarkerSetup.cs: -------------------------------------------------------------------------------- 1 | namespace DynamicVNET.Lib 2 | { 3 | public delegate void MarkerSetup(ITypeRuleMarker ruleMarker); 4 | } 5 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/Core/Validator/Common/ValidatorContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DynamicVNET.Lib.Core 4 | { 5 | /// 6 | /// Context of validator for encapsulation related objects. 7 | /// 8 | public class ValidatorContext 9 | { 10 | public Applier Applier { get; } 11 | 12 | public Type TaggedType { get; } 13 | 14 | /// 15 | /// Constructor of ValidatorContext class. 16 | /// 17 | /// 18 | public ValidatorContext(Applier applier, Type taggedType) 19 | { 20 | this.Applier = applier ?? throw new ArgumentNullException(nameof(applier)); 21 | this.TaggedType = taggedType ?? throw new ArgumentNullException(nameof(taggedType)); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/Core/Validator/ProfileValidator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace DynamicVNET.Lib.Core.Validator 6 | { 7 | /// 8 | /// 9 | /// 10 | public sealed class ProfileValidator 11 | { 12 | private Dictionary _profiles; 13 | 14 | /// 15 | /// 16 | /// 17 | public ProfileValidator() 18 | { 19 | _profiles = new Dictionary(); 20 | } 21 | 22 | /// 23 | /// 24 | /// 25 | /// 26 | /// 27 | /// 28 | /// 29 | /// 30 | public void AddByProfile(string profileName, 31 | MarkerSetup setup, 32 | bool failFirst = false) 33 | { 34 | if (string.IsNullOrEmpty(profileName)) 35 | { 36 | throw new ArgumentNullException("profileName"); 37 | } 38 | 39 | IValidator validator = ValidatorFactory.Create(setup, failFirst); 40 | 41 | _profiles.Add(profileName, validator); 42 | } 43 | 44 | /// 45 | /// 46 | /// 47 | /// 48 | /// 49 | /// 50 | public bool IsValid(string profileName, object instance) 51 | { 52 | IValidator byProfile = GetByProfile(profileName); 53 | CheckInstance(byProfile, instance); 54 | return byProfile.IsValid(instance); 55 | } 56 | 57 | /// 58 | /// 59 | /// 60 | /// 61 | /// 62 | /// 63 | public IEnumerable Validate(string profileName, object instance) 64 | { 65 | IValidator byProfile = GetByProfile(profileName); 66 | CheckInstance(byProfile, instance); 67 | return byProfile.Validate(instance); 68 | } 69 | 70 | private IValidator GetByProfile(string profileName) 71 | { 72 | if (!_profiles.TryGetValue(profileName, out var value)) 73 | { 74 | throw new Exception(profileName + " Not containe!"); 75 | } 76 | 77 | return value; 78 | } 79 | 80 | private void CheckInstance(IValidator validator, object instance) 81 | { 82 | Type type = instance.GetType(); 83 | 84 | if (validator.ValidateType != type) 85 | { 86 | throw new Exception("Invalid " + type.Name + "!"); 87 | } 88 | } 89 | 90 | private IEnumerable GetValidatorForInstace(object instance) 91 | { 92 | return from x in _profiles 93 | where x.Value.ValidateType == instance.GetType() 94 | select x.Value; 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/Core/Validator/Validator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using DynamicVNET.Lib.Core; 5 | using DynamicVNET.Lib.Internal; 6 | 7 | namespace DynamicVNET.Lib 8 | { 9 | public class Validator : IValidator 10 | { 11 | private readonly IEnumerable _validationRules; 12 | 13 | protected readonly ValidatorContext Context; 14 | 15 | public Type ValidateType => this.Context.TaggedType; 16 | 17 | /// 18 | /// Initializes a new instance of the class. 19 | /// 20 | /// 21 | internal Validator(IEnumerable validationRules, ValidatorContext context) 22 | { 23 | this._validationRules = validationRules ?? throw new ArgumentNullException(nameof(validationRules)); 24 | this.Context = context ?? throw new ArgumentNullException(nameof(ValidatorContext)); 25 | } 26 | 27 | /// 28 | public bool IsValid(object instance) 29 | { 30 | if (!instance.GetType().Equals(Context.TaggedType)) 31 | { 32 | throw new ArgumentException("Instance type is invalid. Or defined type is not compatible with tagged one!"); 33 | } 34 | 35 | bool? isAny = Validate(instance)? 36 | .Any(x => !x.IsValid); 37 | 38 | return isAny.HasValue && isAny.Value ? false : true; 39 | } 40 | 41 | /// 42 | public IEnumerable Validate(object instance) 43 | { 44 | return this.Context.Applier.Apply(_validationRules, instance); 45 | } 46 | } 47 | 48 | public sealed class Validator : Validator, IValidator 49 | { 50 | /// 51 | /// Initializes a new instance of the class. 52 | /// 53 | public Validator(IEnumerable validationRules, 54 | ValidatorContext context) : base(validationRules, context) { } 55 | 56 | /// 57 | public bool IsValid(T instance) 58 | { 59 | return IsValid((object)instance); 60 | } 61 | 62 | /// 63 | public IEnumerable Validate(T instance) 64 | { 65 | return Validate((object)instance); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/Core/Validator/ValidatorFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using DynamicVNET.Lib.Core; 3 | 4 | namespace DynamicVNET.Lib 5 | { 6 | public static class ValidatorFactory 7 | { 8 | /// 9 | /// It helps to create/initiate a validator instance for a specific type of validation. 10 | /// 11 | /// Type of validation 12 | /// In case of setup argument is empty. 13 | public static IValidator Create(MarkerSetup setup, bool failFirst = false) 14 | { 15 | if (setup == null) 16 | { 17 | throw new ArgumentNullException(nameof(setup)); 18 | } 19 | 20 | var ruleMarker = new RuleMarker(); 21 | setup(ruleMarker); 22 | 23 | var applier = failFirst ? new FailFirstApplier() : new DefaultApplier(); 24 | 25 | var validatorContext = new ValidatorContext(applier, typeof(T)); 26 | 27 | return new Validator(ruleMarker.Rules, validatorContext); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/DynamicVNET.Lib.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0;net461;net5.0 5 | DynamicVNET 6 | 1.4.1 7 | 1.4.1 8 | Rasul Huseynov 9 | 10 | DynamicVNET is a .NET Standard library that was created to develop dynamic reuse validation. The main idea of the library is to apply validation rules using a declarative approach. And the rules can be used on POCO and BlackBox libraries. Also, it has rich facilities and features as Fluent API at runtime. 11 | 12 | DynamicVNET.Lib 13 | Copyright (c) 2018-2024 Rasul Huseynov 14 | https://github.com/rasulhsn/DynamicVNET/blob/master/LICENSE 15 | https://github.com/rasulhsn/DynamicVNET 16 | https://github.com/rasulhsn/DynamicVNET 17 | POCOValidation DataAnnotation Validator BindingDynamicValidation DynamicValidator FluentValidation 18 | True 19 | DynamicVNET Icon.png 20 | README.md 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | True 30 | \ 31 | 32 | 33 | True 34 | \ 35 | 36 | 37 | 38 | 39 | 40 | all 41 | runtime; build; native; contentfiles; analyzers; buildtransitive 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/ExtendAssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using System.Runtime.CompilerServices; 3 | 4 | 5 | [assembly: ComVisible(false)] 6 | 7 | [assembly: InternalsVisibleTo("DynamicVNET.Lib.Unit.Tests")] 8 | [assembly: InternalsVisibleTo("DynamicVNET.Lib.Integration.Tests")] 9 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/Helper/Utility.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | 4 | namespace DynamicVNET.Lib.Helper 5 | { 6 | internal static class Utility 7 | { 8 | /// 9 | /// Wrapper to all marking operations. 10 | /// 11 | /// The body. 12 | /// Name of the mark. 13 | /// Occurred error in {markName}! 14 | public static void TrackTrace(Action body, string markName) 15 | { 16 | Trace.WriteLine($"{typeof(T)} Entry {nameof(TrackTrace)} -> {markName}"); 17 | 18 | try 19 | { 20 | body(); 21 | } 22 | catch (Exception exp) 23 | { 24 | Trace.WriteLine($"{exp.Message} || {exp.StackTrace} || {exp.InnerException?.Message}"); 25 | throw; 26 | } 27 | } 28 | 29 | /// 30 | /// Wrapper to all marking operations. 31 | /// 32 | /// The body. 33 | /// Name of the mark. 34 | /// Occurred error in {markName}! 35 | public static void TrackTrace(Action body, string markName) 36 | { 37 | Trace.WriteLine($"Entry {nameof(TrackTrace)} -> {markName}"); 38 | 39 | try 40 | { 41 | body(); 42 | } 43 | catch (Exception exp) 44 | { 45 | Trace.WriteLine($"{exp.Message} || {exp.StackTrace} || {exp.InnerException?.Message}"); 46 | throw; 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/MemberRuleExtensions.cs: -------------------------------------------------------------------------------- 1 | using DynamicVNET.Lib.Core; 2 | using System; 3 | 4 | namespace DynamicVNET.Lib 5 | { 6 | public static class MemberRuleExtensions 7 | { 8 | /// 9 | /// Marker Null with specified error message. 10 | /// 11 | /// The builder. 12 | /// The error message. 13 | public static IMemberRuleMarker Null(this IMemberRuleMarker builder, string errorMessage = RuleExtensions.ERROR_NULL) 14 | { 15 | builder.Null(builder.Selected, errorMessage); 16 | return builder; 17 | } 18 | 19 | /// 20 | /// Marker Not null with specified error message. 21 | /// 22 | /// The builder. 23 | /// The error message. 24 | public static IMemberRuleMarker NotNull(this IMemberRuleMarker builder, string errorMessage = RuleExtensions.ERROR_NOT_NULL) 25 | { 26 | builder.NotNull(builder.Selected, errorMessage); 27 | return builder; 28 | } 29 | 30 | /// 31 | /// Marker String length. 32 | /// 33 | /// The builder. 34 | /// The maximum len. 35 | /// The minimum len. 36 | public static IMemberRuleMarker StringLen(this IMemberRuleMarker builder, int max, int? min = null) 37 | { 38 | builder.StringLen(builder.Selected, max, min); 39 | return builder; 40 | } 41 | 42 | 43 | /// 44 | /// Marker Regular expressions. 45 | /// 46 | /// The builder. 47 | /// The pattern. 48 | public static IMemberRuleMarker RegularExp(this IMemberRuleMarker builder, string pattern) 49 | { 50 | builder.RegularExp(builder.Selected, nameof(RegularExp), pattern); 51 | return builder; 52 | } 53 | 54 | 55 | /// 56 | /// Marker Required 57 | /// 58 | /// The builder. 59 | public static IMemberRuleMarker Required(this IMemberRuleMarker builder) 60 | { 61 | builder.Required(builder.Selected); 62 | return builder; 63 | } 64 | 65 | 66 | /// 67 | /// Marker Maximum length. 68 | /// 69 | /// The builder. 70 | /// The length. 71 | public static IMemberRuleMarker MaxLen(this IMemberRuleMarker builder, int length) 72 | { 73 | builder.MaxLen(builder.Selected, length); 74 | return builder; 75 | } 76 | 77 | 78 | /// 79 | /// Marker Range with specified minimum. 80 | /// 81 | /// The builder. 82 | /// The minimum. 83 | /// The maximum. 84 | public static IMemberRuleMarker Range(this IMemberRuleMarker builder, int min, int max) 85 | { 86 | builder.Range(builder.Selected, min, max); 87 | return builder; 88 | } 89 | 90 | 91 | /// 92 | /// Marker Range with specified minimum. 93 | /// 94 | /// The builder. 95 | /// The minimum. 96 | /// The maximum. 97 | public static IMemberRuleMarker Range(this IMemberRuleMarker builder, double min, double max) 98 | { 99 | builder.Range(builder.Selected, min, max); 100 | return builder; 101 | } 102 | 103 | 104 | /// 105 | /// Marker Range with specified type. 106 | /// 107 | /// The builder. 108 | /// The type. 109 | /// The minimum. 110 | /// The maximum. 111 | public static IMemberRuleMarker Range(this IMemberRuleMarker builder, Type type, string min, string max) 112 | { 113 | builder.Range(builder.Selected, type, min, max); 114 | return builder; 115 | } 116 | 117 | /// 118 | /// Marker Email address. 119 | /// 120 | /// The builder. 121 | public static IMemberRuleMarker EmailAddress(this IMemberRuleMarker builder) 122 | { 123 | builder.EmailAddress(builder.Selected); 124 | return builder; 125 | } 126 | 127 | 128 | /// 129 | /// Marker URL. 130 | /// 131 | /// The builder. 132 | public static IMemberRuleMarker Url(this IMemberRuleMarker builder) 133 | { 134 | builder.Url(builder.Selected); 135 | return builder; 136 | } 137 | 138 | /// 139 | /// 140 | /// 141 | /// 142 | /// 143 | /// 144 | public static IMemberRuleMarker GreaterThan(this IMemberRuleMarker builder, int minValue , string errorMessage = null) 145 | { 146 | errorMessage = errorMessage ?? $"Value of the instance should greather than {minValue}"; 147 | builder.GreaterThan(builder.Selected, minValue, errorMessage); 148 | return builder; 149 | } 150 | 151 | /// 152 | /// 153 | /// 154 | /// 155 | /// 156 | /// 157 | public static IMemberRuleMarker LessThan(this IMemberRuleMarker builder, int maxValue, string errorMessage = null) 158 | { 159 | errorMessage = errorMessage ?? $"Value of the instance should less than {maxValue}"; 160 | builder.LessThan(builder.Selected, maxValue, errorMessage); 161 | return builder; 162 | } 163 | 164 | /// 165 | /// 166 | /// 167 | /// 168 | /// 169 | /// 170 | public static IMemberRuleMarker GreaterThan(this IMemberRuleMarker builder, double minValue, string errorMessage = null) 171 | { 172 | errorMessage = errorMessage ?? $"Value of the instance should greather than {minValue}"; 173 | builder.GreaterThan(builder.Selected, minValue, errorMessage); 174 | return builder; 175 | } 176 | 177 | /// 178 | /// 179 | /// 180 | /// 181 | /// 182 | /// 183 | public static IMemberRuleMarker LessThan(this IMemberRuleMarker builder, double maxValue, string errorMessage = null) 184 | { 185 | errorMessage = errorMessage ?? $"Value of the instance should less than {maxValue}"; 186 | builder.LessThan(builder.Selected, maxValue, errorMessage); 187 | return builder; 188 | } 189 | 190 | /// 191 | /// 192 | /// 193 | /// 194 | /// 195 | /// 196 | public static IMemberRuleMarker GreaterThan(this IMemberRuleMarker builder, decimal minValue, string errorMessage = null) 197 | { 198 | errorMessage = errorMessage ?? $"Value of the instance should greather than {minValue}"; 199 | builder.GreaterThan(builder.Selected, minValue, errorMessage); 200 | return builder; 201 | } 202 | 203 | /// 204 | /// 205 | /// 206 | /// 207 | /// 208 | /// 209 | public static IMemberRuleMarker LessThan(this IMemberRuleMarker builder, decimal maxValue, string errorMessage = null) 210 | { 211 | errorMessage = errorMessage ?? $"Value of the instance should less than {maxValue}"; 212 | builder.LessThan(builder.Selected, maxValue, errorMessage); 213 | return builder; 214 | } 215 | 216 | /// 217 | /// 218 | /// 219 | /// 220 | /// 221 | /// 222 | public static IMemberRuleMarker GreaterThan(this IMemberRuleMarker builder, float minValue, string errorMessage = null) 223 | { 224 | errorMessage = errorMessage ?? $"Value of the instance should greather than {minValue}"; 225 | builder.GreaterThan(builder.Selected, minValue, errorMessage); 226 | return builder; 227 | } 228 | 229 | /// 230 | /// 231 | /// 232 | /// 233 | /// 234 | /// 235 | public static IMemberRuleMarker LessThan(this IMemberRuleMarker builder, float maxValue, string errorMessage = null) 236 | { 237 | errorMessage = errorMessage ?? $"Value of the instance should less than {maxValue}"; 238 | builder.LessThan(builder.Selected, maxValue, errorMessage); 239 | return builder; 240 | } 241 | 242 | /// 243 | /// 244 | /// 245 | /// 246 | /// 247 | /// 248 | public static IMemberRuleMarker GreaterThan(this IMemberRuleMarker builder, byte minValue, string errorMessage = null) 249 | { 250 | errorMessage = errorMessage ?? $"Value of the instance should greather than {minValue}"; 251 | builder.GreaterThan(builder.Selected, minValue, errorMessage); 252 | return builder; 253 | } 254 | 255 | /// 256 | /// 257 | /// 258 | /// 259 | /// 260 | /// 261 | public static IMemberRuleMarker LessThan(this IMemberRuleMarker builder, byte maxValue, string errorMessage = null) 262 | { 263 | errorMessage = errorMessage ?? $"Value of the instance should less than {maxValue}"; 264 | builder.LessThan(builder.Selected, maxValue, errorMessage); 265 | return builder; 266 | } 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /DynamicVNET.Lib/TypeRuleExtensions.cs: -------------------------------------------------------------------------------- 1 | using DynamicVNET.Lib.Helper; 2 | using DynamicVNET.Lib.Internal; 3 | using DynamicVNET.Lib.Core; 4 | using System; 5 | using System.Linq.Expressions; 6 | 7 | namespace DynamicVNET.Lib 8 | { 9 | public static class TypeRuleExtensions 10 | { 11 | /// 12 | /// Marker For the specified member. 13 | /// 14 | /// 15 | /// The type of the member. 16 | /// The builder. 17 | /// The member. 18 | public static IMemberRuleMarker For(this ITypeRuleMarker builder, Expression> member) 19 | { 20 | var expMember = ExpressionMemberFactory.Create(member); 21 | 22 | IMemberRuleMarker memberBuilder = builder as IMemberRuleMarker; 23 | 24 | if (memberBuilder != null) 25 | { 26 | memberBuilder.Selected = expMember; 27 | } 28 | 29 | return memberBuilder; 30 | } 31 | 32 | /// 33 | /// Marker Null. 34 | /// 35 | /// 36 | /// The type of the member. 37 | /// The builder. 38 | /// The member. 39 | /// The error message. 40 | public static ITypeRuleMarker Null(this ITypeRuleMarker builder, Expression> member, string errorMessage = RuleExtensions.ERROR_NULL) where TMember : class 41 | { 42 | Utility.TrackTrace(() => 43 | { 44 | var expMember = ExpressionMemberFactory.Create(member); 45 | builder.Null(expMember, errorMessage); 46 | }, nameof(Null)); 47 | return builder; 48 | } 49 | 50 | /// 51 | /// Marker Not null. 52 | /// 53 | /// 54 | /// The type of the member. 55 | /// The builder. 56 | /// The member. 57 | /// The error message. 58 | public static ITypeRuleMarker NotNull(this ITypeRuleMarker builder, Expression> member, string errorMessage = RuleExtensions.ERROR_NOT_NULL) where TMember : class 59 | { 60 | Utility.TrackTrace(() => 61 | { 62 | var expMember = ExpressionMemberFactory.Create(member); 63 | builder.NotNull(expMember, errorMessage); 64 | }, nameof(NotNull)); 65 | return builder; 66 | } 67 | 68 | /// 69 | /// Marker Predicate with specified condition. 70 | /// 71 | /// 72 | /// The builder. 73 | /// The condition. 74 | /// The first error message. 75 | public static ITypeRuleMarker Predicate(this ITypeRuleMarker builder, Func condition, string firstErrorMessage = RuleExtensions.ERROR_CUSTOM) 76 | { 77 | Utility.TrackTrace(() => 78 | { 79 | var expMember = ExpressionMemberFactory.Create(); 80 | builder.Add(new PredicateRule(condition, firstErrorMessage, new RuleContext("Custom", expMember))); 81 | }, nameof(Predicate)); 82 | return builder; 83 | } 84 | 85 | /// 86 | /// Marker String length 87 | /// 88 | /// 89 | /// The type of the member. 90 | /// The builder. 91 | /// The member. 92 | /// The maximum len. 93 | /// The minimum len. 94 | public static ITypeRuleMarker StringLen(this ITypeRuleMarker builder, Expression> member, int max, int? min = null) 95 | { 96 | Utility.TrackTrace(() => 97 | { 98 | var expMember = ExpressionMemberFactory.Create(member); 99 | builder.StringLen(expMember, max, min); 100 | }, nameof(StringLen)); 101 | return builder; 102 | } 103 | 104 | /// 105 | /// Marker Email address. 106 | /// 107 | /// 108 | /// The type of the member. 109 | /// The builder. 110 | /// The member. 111 | public static ITypeRuleMarker EmailAddress(this ITypeRuleMarker builder, Expression> member) 112 | { 113 | Utility.TrackTrace(() => 114 | { 115 | var expMember = ExpressionMemberFactory.Create(member); 116 | builder.RegularExp(expMember, nameof(EmailAddress), RuleExtensions.EMAIL_PATTERN); 117 | }, nameof(EmailAddress)); 118 | return builder; 119 | } 120 | 121 | /// 122 | /// Marker URL. 123 | /// 124 | /// 125 | /// The type of the member. 126 | /// The builder. 127 | /// The member. 128 | public static ITypeRuleMarker Url(this ITypeRuleMarker builder, Expression> member) 129 | { 130 | Utility.TrackTrace(() => 131 | { 132 | var expMember = ExpressionMemberFactory.Create(member); 133 | builder.RegularExp(expMember, nameof(Url), RuleExtensions.URL_PATTERN); 134 | }, nameof(Url)); 135 | return builder; 136 | } 137 | 138 | /// 139 | /// Marker Required. 140 | /// 141 | /// 142 | /// The type of the member. 143 | /// The builder. 144 | /// The member. 145 | public static ITypeRuleMarker Required(this ITypeRuleMarker builder, Expression> member) 146 | { 147 | Utility.TrackTrace(() => 148 | { 149 | var expMember = ExpressionMemberFactory.Create(member); 150 | builder.Required(expMember); 151 | }, nameof(Required)); 152 | return builder; 153 | } 154 | 155 | /// 156 | /// Marker Maximum length. 157 | /// 158 | /// 159 | /// The type of the member. 160 | /// The builder. 161 | /// The member. 162 | /// The length. 163 | public static ITypeRuleMarker MaxLen(this ITypeRuleMarker builder, Expression> member, int length = 0) 164 | { 165 | Utility.TrackTrace(() => 166 | { 167 | var expMember = ExpressionMemberFactory.Create(member); 168 | builder.MaxLen(expMember, length); 169 | }, nameof(MaxLen)); 170 | return builder; 171 | } 172 | 173 | /// 174 | /// Marker Regular expressions. 175 | /// 176 | /// 177 | /// The type of the member. 178 | /// The builder. 179 | /// The member. 180 | /// The pattern. 181 | public static ITypeRuleMarker RegularExp(this ITypeRuleMarker builder, Expression> member, string pattern) 182 | { 183 | Utility.TrackTrace(() => 184 | { 185 | var expMember = ExpressionMemberFactory.Create(member); 186 | builder.RegularExp(expMember, nameof(RegularExp), pattern); 187 | }, nameof(RegularExp)); 188 | return builder; 189 | } 190 | 191 | /// 192 | /// Marker Range. 193 | /// 194 | /// 195 | /// The type of the member. 196 | /// The builder. 197 | /// The member. 198 | /// The minimum. 199 | /// The maximum. 200 | public static ITypeRuleMarker Range(this ITypeRuleMarker builder, Expression> member, double min, double max) 201 | { 202 | Utility.TrackTrace(() => 203 | { 204 | var expMember = ExpressionMemberFactory.Create(member); 205 | builder.Range(expMember, min, max); 206 | }, nameof(Range)); 207 | return builder; 208 | } 209 | 210 | /// 211 | /// Marker Range. 212 | /// 213 | /// 214 | /// The type of the member. 215 | /// The builder. 216 | /// The member. 217 | /// The minimum. 218 | /// The maximum. 219 | public static ITypeRuleMarker Range(this ITypeRuleMarker builder, Expression> member, int min, int max) 220 | { 221 | Utility.TrackTrace(() => 222 | { 223 | var expMember = ExpressionMemberFactory.Create(member); 224 | builder.Range(expMember, min, max); 225 | }, nameof(Range)); 226 | return builder; 227 | } 228 | 229 | 230 | /// 231 | /// Marker Range. 232 | /// 233 | /// 234 | /// The type of the member. 235 | /// The builder. 236 | /// The member. 237 | /// The type. 238 | /// The minimum. 239 | /// The maximum. 240 | public static ITypeRuleMarker Range(this ITypeRuleMarker builder, Expression> member, Type type, string min, string max) 241 | { 242 | Utility.TrackTrace(() => 243 | { 244 | var expMember = ExpressionMemberFactory.Create(member); 245 | builder.Range(expMember, type, min, max); 246 | }, nameof(Range)); 247 | return builder; 248 | } 249 | 250 | /// 251 | /// Marker Branch with specified condition. 252 | /// 253 | /// 254 | /// The builder. 255 | /// The condition. 256 | /// The setup. 257 | public static ITypeRuleMarker Branch(this ITypeRuleMarker builder, Func condition, Action> setup) 258 | { 259 | Utility.TrackTrace(() => 260 | { 261 | if (setup == null) 262 | { 263 | throw new ArgumentNullException(nameof(setup) + " can not be null in " + nameof(Branch)); 264 | } 265 | 266 | var nestedMarker = new RuleMarker(); 267 | setup.Invoke(nestedMarker); 268 | 269 | builder.Add(new BranchRule(nestedMarker.Rules, 270 | condition, 271 | new RuleContext(nameof(Branch), 272 | new EmptyMember(condition.ToString(), 273 | typeof(T))))); 274 | }, nameof(Branch)); 275 | return builder; 276 | } 277 | 278 | /// 279 | /// 280 | /// 281 | /// 282 | /// 283 | /// 284 | /// 285 | /// 286 | public static ITypeRuleMarker GreaterThan(this ITypeRuleMarker builder, Expression> member, int minValue, string errorMessage = null) 287 | { 288 | Utility.TrackTrace(() => 289 | { 290 | errorMessage = errorMessage ?? $"Value of the instance should greather than {minValue}"; 291 | var expMember = ExpressionMemberFactory.Create(member); 292 | builder.GreaterThan(expMember, minValue, errorMessage); 293 | }, nameof(GreaterThan)); 294 | return builder; 295 | } 296 | 297 | /// 298 | /// 299 | /// 300 | /// 301 | /// 302 | /// 303 | /// 304 | /// 305 | public static ITypeRuleMarker LessThan(this ITypeRuleMarker builder, Expression> member, int maxValue, string errorMessage = null) 306 | { 307 | Utility.TrackTrace(() => 308 | { 309 | errorMessage = errorMessage ?? $"Value of the instance should less than {maxValue}"; 310 | var expMember = ExpressionMemberFactory.Create(member); 311 | builder.LessThan(expMember, maxValue, errorMessage); 312 | }, nameof(LessThan)); 313 | return builder; 314 | } 315 | 316 | 317 | /// 318 | /// 319 | /// 320 | /// 321 | /// 322 | /// 323 | /// 324 | /// 325 | public static ITypeRuleMarker GreaterThan(this ITypeRuleMarker builder, Expression> member, double minValue, string errorMessage = null) 326 | { 327 | Utility.TrackTrace(() => 328 | { 329 | errorMessage = errorMessage ?? $"Value of the instance should greather than {minValue}"; 330 | var expMember = ExpressionMemberFactory.Create(member); 331 | builder.GreaterThan(expMember, minValue, errorMessage); 332 | }, nameof(GreaterThan)); 333 | return builder; 334 | } 335 | 336 | /// 337 | /// 338 | /// 339 | /// 340 | /// 341 | /// 342 | /// 343 | /// 344 | public static ITypeRuleMarker LessThan(this ITypeRuleMarker builder, Expression> member, double maxValue, string errorMessage = null) 345 | { 346 | Utility.TrackTrace(() => 347 | { 348 | errorMessage = errorMessage ?? $"Value of the instance should less than {maxValue}"; 349 | var expMember = ExpressionMemberFactory.Create(member); 350 | builder.LessThan(expMember, maxValue, errorMessage); 351 | }, nameof(LessThan)); 352 | return builder; 353 | } 354 | 355 | /// 356 | /// 357 | /// 358 | /// 359 | /// 360 | /// 361 | /// 362 | /// 363 | public static ITypeRuleMarker GreaterThan(this ITypeRuleMarker builder, Expression> member, decimal minValue, string errorMessage = null) 364 | { 365 | Utility.TrackTrace(() => 366 | { 367 | errorMessage = errorMessage ?? $"Value of the instance should greather than {minValue}"; 368 | var expMember = ExpressionMemberFactory.Create(member); 369 | builder.GreaterThan(expMember, minValue, errorMessage); 370 | }, nameof(GreaterThan)); 371 | return builder; 372 | } 373 | 374 | /// 375 | /// 376 | /// 377 | /// 378 | /// 379 | /// 380 | /// 381 | /// 382 | public static ITypeRuleMarker LessThan(this ITypeRuleMarker builder, Expression> member, decimal maxValue, string errorMessage = null) 383 | { 384 | Utility.TrackTrace(() => 385 | { 386 | errorMessage = errorMessage ?? $"Value of the instance should less than {maxValue}"; 387 | var expMember = ExpressionMemberFactory.Create(member); 388 | builder.LessThan(expMember, maxValue, errorMessage); 389 | }, nameof(LessThan)); 390 | return builder; 391 | } 392 | 393 | /// 394 | /// 395 | /// 396 | /// 397 | /// 398 | /// 399 | /// 400 | /// 401 | public static ITypeRuleMarker GreaterThan(this ITypeRuleMarker builder, Expression> member, float minValue, string errorMessage = null) 402 | { 403 | Utility.TrackTrace(() => 404 | { 405 | errorMessage = errorMessage ?? $"Value of the instance should greather than {minValue}"; 406 | var expMember = ExpressionMemberFactory.Create(member); 407 | builder.GreaterThan(expMember, minValue, errorMessage); 408 | }, nameof(GreaterThan)); 409 | return builder; 410 | } 411 | 412 | /// 413 | /// 414 | /// 415 | /// 416 | /// 417 | /// 418 | /// 419 | /// 420 | public static ITypeRuleMarker LessThan(this ITypeRuleMarker builder, Expression> member, float maxValue, string errorMessage = null) 421 | { 422 | Utility.TrackTrace(() => 423 | { 424 | errorMessage = errorMessage ?? $"Value of the instance should less than {maxValue}"; 425 | var expMember = ExpressionMemberFactory.Create(member); 426 | builder.LessThan(expMember, maxValue, errorMessage); 427 | }, nameof(LessThan)); 428 | return builder; 429 | } 430 | 431 | /// 432 | /// 433 | /// 434 | /// 435 | /// 436 | /// 437 | /// 438 | /// 439 | public static ITypeRuleMarker GreaterThan(this ITypeRuleMarker builder, Expression> member, byte minValue, string errorMessage = null) 440 | { 441 | Utility.TrackTrace(() => 442 | { 443 | errorMessage = errorMessage ?? $"Value of the instance should greather than {minValue}"; 444 | var expMember = ExpressionMemberFactory.Create(member); 445 | builder.GreaterThan(expMember, minValue, errorMessage); 446 | }, nameof(GreaterThan)); 447 | return builder; 448 | } 449 | 450 | /// 451 | /// 452 | /// 453 | /// 454 | /// 455 | /// 456 | /// 457 | /// 458 | public static ITypeRuleMarker LessThan(this ITypeRuleMarker builder, Expression> member, byte maxValue, string errorMessage = null) 459 | { 460 | Utility.TrackTrace(() => 461 | { 462 | errorMessage = errorMessage ?? $"Value of the instance should less than {maxValue}"; 463 | var expMember = ExpressionMemberFactory.Create(member); 464 | builder.LessThan(expMember, maxValue, errorMessage); 465 | }, nameof(LessThan)); 466 | return builder; 467 | } 468 | } 469 | } 470 | -------------------------------------------------------------------------------- /DynamicVNET.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.11.35222.181 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DynamicVNET.Lib", "DynamicVNET.Lib\DynamicVNET.Lib.csproj", "{0B85F0E6-29BE-42FB-8865-92AE79DDE3BD}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DynamicVNET.Lib.Unit.Tests", "DynamicVNET.Lib.Unit.Tests\DynamicVNET.Lib.Unit.Tests.csproj", "{1DB9D509-0D13-4924-8F7D-CFB2D5E070DC}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DynamicVNET.Lib.Integration.Tests", "DynamicVNET.Lib.Integration.Tests\DynamicVNET.Lib.Integration.Tests.csproj", "{979426F2-C11F-4B20-8A99-EFB681767F30}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DynamicVNET.Lib.Benchmarks", "DynamicVNET.Lib.Benchmarks\DynamicVNET.Lib.Benchmarks.csproj", "{D2D626DC-D8E4-4348-8E05-D12D10845C2C}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {0B85F0E6-29BE-42FB-8865-92AE79DDE3BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {0B85F0E6-29BE-42FB-8865-92AE79DDE3BD}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {0B85F0E6-29BE-42FB-8865-92AE79DDE3BD}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {0B85F0E6-29BE-42FB-8865-92AE79DDE3BD}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {1DB9D509-0D13-4924-8F7D-CFB2D5E070DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {1DB9D509-0D13-4924-8F7D-CFB2D5E070DC}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {1DB9D509-0D13-4924-8F7D-CFB2D5E070DC}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {1DB9D509-0D13-4924-8F7D-CFB2D5E070DC}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {979426F2-C11F-4B20-8A99-EFB681767F30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {979426F2-C11F-4B20-8A99-EFB681767F30}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {979426F2-C11F-4B20-8A99-EFB681767F30}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {979426F2-C11F-4B20-8A99-EFB681767F30}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {D2D626DC-D8E4-4348-8E05-D12D10845C2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {D2D626DC-D8E4-4348-8E05-D12D10845C2C}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {D2D626DC-D8E4-4348-8E05-D12D10845C2C}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {D2D626DC-D8E4-4348-8E05-D12D10845C2C}.Release|Any CPU.Build.0 = Release|Any CPU 36 | EndGlobalSection 37 | GlobalSection(SolutionProperties) = preSolution 38 | HideSolutionNode = FALSE 39 | EndGlobalSection 40 | GlobalSection(ExtensibilityGlobals) = postSolution 41 | SolutionGuid = {C20CE0AF-1091-447F-9191-861121560962} 42 | EndGlobalSection 43 | EndGlobal 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2021 Rasul Huseynov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## DynamicVNET - Overview 2 | [![NuGet](https://img.shields.io/badge/nuget-1.4.1-blue.svg)](https://www.nuget.org/packages/DynamicVNET/1.4.1) 3 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/rasulhsn/DynamicVNET/blob/master/LICENSE) 4 | 5 | DynamicVNET is a **.NET Standard library** designed to provide reusable, dynamic validation. It allows developers to build custom validation rules for **POCOs** (Plain Old CLR Objects) and integrate validation into private or third-party libraries seamlessly. With its **Fluent API** interface, wrapped over **DataAnnotation** attributes, DynamicVNET delivers a rich and flexible validation library for cross-platform environments. 6 | 7 | ## Key Features 8 | - **Branching & Nested Branching**: Create complex validation rules with ease. 9 | - **Nested Members**: Validate properties of nested objects. 10 | - **Value Types & Single Primitives**: Validate scalar types such as integers and strings. 11 | - **Reference Types**: Validate custom objects and classes. 12 | - **Automatic Repeated Validation Ignoring**: Avoid redundant validation checks. 13 | - **Strongly Self Validator**: Leverage inheritance for reusable and self-contained validators. 14 | 15 | ## Use Cases 16 | DynamicVNET is suitable for a variety of scenarios, including: 17 | - **POCO Validation**: Define and enforce validation rules for simple objects. 18 | - **Dynamic Validation for Libraries**: Validate objects in private or third-party libraries (e.g., `.dll` files). 19 | 20 | ## Validation Methods 21 | DynamicVNET supports a wide range of validation methods: 22 | - `Predicate` (Custom logic) 23 | - `StringLen` (String length) 24 | - `EmailAddress` (Email validation) 25 | - `Url` (Validates URLs for GET requests) 26 | - `Required` (Ensures non-null or non-empty values) 27 | - `MaxLen` (Maximum string length) 28 | - `RegularExp` (Regex-based validation) 29 | - `Range` (Range validation) 30 | - `Null` (Only for reference types) 31 | - `NotNull` (Only for reference types) 32 | - `GreaterThan` (Numeric comparison) 33 | - `LessThan` (Numeric comparison) 34 | 35 | ## Getting Started 36 | 37 | ```mermaid 38 | flowchart LR 39 | A[POCO Object] --> B[ValidatorFactory] 40 | B --> C[Fluent API Configuration] 41 | C --> D[Validation Rules] 42 | D -->|Required| E[Validator] 43 | D -->|StringLen| E 44 | D -->|Predicate| E 45 | E --> F[Validation Results] 46 | F -->|IsValid| G[Boolean Result] 47 | F -->|Detailed Results| H[ValidationMarkerResult] 48 | ``` 49 | 50 | ```csharp 51 | public class Employee 52 | { 53 | public string Name { get; set; } 54 | public Token TokenNumber { get; set; } 55 | public string Email { get; set; } 56 | 57 | } 58 | public class Token 59 | { 60 | public string Number { get; set; } 61 | } 62 | ``` 63 | 64 | ```csharp 65 | Employee emp = new Employee() 66 | { 67 | Name = "Jhon", 68 | TokenNumber = new Token() { Number = "2312412312341" }, 69 | Email = "jhon.sim@gmail.com" 70 | }; 71 | 72 | var validator = ValidatorFactory.Create(builder => { 73 | builder.StringLen(x => x.Name, 4) 74 | .EmailAddress(x => x.Email) 75 | .Predicate(x => x.Email.Contains("@simple.com")) 76 | .Required(x => x.TokenNumber.Number) // nested member 77 | .Required(x => x.TokenNumber.Number); // automatic ignored 78 | }); 79 | 80 | bool result = validator.IsValid(emp); 81 | ``` 82 | 83 | ```csharp 84 | // Detailed Result 85 | IEnumerable results = validator.Validate(emp); 86 | ``` 87 | #### Branch Example 88 | ```csharp 89 | var validator = ValidatorFactory.Create(builder => { 90 | builder.Required(x => x.Token.TokenNumber) 91 | .Branch(x => x.Name.Contains("resul"), x => 92 | { 93 | x.Required(y => y.Email) 94 | .StringLen(y => y.Email, 2) 95 | .Branch(n => n.Name.Length >= 4,n => { 96 | n.MaxLen(s => s.Token.TokenNumber, length: 4); 97 | }); 98 | }).Branch(x => x.Email.Contains("aa"), x => 99 | { 100 | x.Required(y => y.Name) 101 | .StringLen(y => y.Name, 5) 102 | .StringLen(y => y.Token.TokenNumber, 9); 103 | }); 104 | }); 105 | ``` 106 | 107 | ### Strongly Validator 108 | 109 | ```csharp 110 | public class EmployeeValidator : BaseValidator 111 | { 112 | protected override void Setup(ValidatorBuilder builder) 113 | { 114 | builder.For(x => x.Name) 115 | .Required(); 116 | 117 | builder.Branch(x => x.Name.Contains("Jhon"), x => 118 | { 119 | x.MaxLen(m => m.TokenNumber.Number, 15); 120 | }) 121 | .For(x => x.Email) 122 | .Required() 123 | .EmailAddress(); 124 | 125 | builder.Required(x => x.TokenNumber.Number); 126 | } 127 | } 128 | 129 | Employee emp = new Employee() 130 | { 131 | Name = "Jhon Simon", 132 | TokenNumber = new Token() { Number = "ASD123123" }, 133 | Email = "jhon.sim@jhona.com" 134 | }; 135 | 136 | var empValidator = new EmployeeValidator(); 137 | bool result = empValidator.IsValid(emp); 138 | ``` 139 | 140 | ### Where can I get it? 141 | 142 | Install [DynamicVNET](https://www.nuget.org/packages/DynamicVNET/) from the package manager console: 143 | 144 | ``` 145 | PM> Install-Package DynamicVNET -Version 1.4.1 146 | ``` 147 | 148 | ### Copyright 149 | 150 | [DynamicVNET](https://github.com/rasulhsn/DynamicVNET) is Copyright © 2018 Rasul Huseynov. 151 | --------------------------------------------------------------------------------