diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 43089c70..f88b97d2 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -22,9 +22,9 @@ body: - "Data.EFCore" - "Data.Security" - "Data.Validation" + - "Data.Validation.FluentValidation" - "Diagnostics" - "Foundation" - - "Media.Image" - "Networking" - "Runtime" - "Testing" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c8148607..50516f42 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,7 @@ jobs: env: BUILD_CONFIG: 'Release' - SOLUTION: 'MADE.NET.NoSamples.slnf' + SOLUTION: 'MADE.NET.sln' runs-on: windows-latest @@ -55,11 +55,8 @@ jobs: with: dotnet-version: 6.0.x - - name: Setup NuGet - uses: NuGet/setup-nuget@v1.0.5 - - name: Restore dependencies - run: nuget restore $SOLUTION + run: dotnet restore $SOLUTION - name: Setup MSBuild uses: microsoft/setup-msbuild@v1.0.2 diff --git a/MADE.NET.NoSamples.slnf b/MADE.NET.NoSamples.slnf deleted file mode 100644 index 890bca2b..00000000 --- a/MADE.NET.NoSamples.slnf +++ /dev/null @@ -1,26 +0,0 @@ -{ - "solution": { - "path": "MADE.NET.sln", - "projects": [ - "src\\MADE.Collections\\MADE.Collections.csproj", - "src\\MADE.Data.Converters\\MADE.Data.Converters.csproj", - "src\\MADE.Data.EFCore\\MADE.Data.EFCore.csproj", - "src\\MADE.Data.Validation\\MADE.Data.Validation.csproj", - "src\\MADE.Diagnostics\\MADE.Diagnostics.csproj", - "src\\MADE.Foundation\\MADE.Foundation.csproj", - "src\\MADE.Media.Image\\MADE.Media.Image.csproj", - "src\\MADE.Networking\\MADE.Networking.csproj", - "src\\MADE.Runtime\\MADE.Runtime.csproj", - "src\\MADE.Testing\\MADE.Testing.csproj", - "src\\MADE.Threading\\MADE.Threading.csproj", - "src\\MADE.Web\\MADE.Web.csproj", - "src\\MADE.Web.Mvc\\MADE.Web.Mvc.csproj", - "tests\\MADE.Collections.Tests\\MADE.Collections.Tests.csproj", - "tests\\MADE.Data.Converters.Tests\\MADE.Data.Converters.Tests.csproj", - "tests\\MADE.Data.Validation.Tests\\MADE.Data.Validation.Tests.csproj", - "tests\\MADE.Diagnostics.Tests\\MADE.Diagnostics.Tests.csproj", - "tests\\MADE.Networking.Tests\\MADE.Networking.Tests.csproj", - "tests\\MADE.Web.Tests\\MADE.Web.Tests.csproj" - ] - } -} \ No newline at end of file diff --git a/MADE.NET.sln b/MADE.NET.sln index 6aa92622..37709251 100644 --- a/MADE.NET.sln +++ b/MADE.NET.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.31624.102 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32228.430 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{01380FB8-F8A7-4416-AABA-5407574B7723}" EndProject @@ -37,14 +37,20 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MADE.Web.Tests", "tests\MAD EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MADE.Web.Mvc", "src\MADE.Web.Mvc\MADE.Web.Mvc.csproj", "{CF632609-DE29-4375-B887-F0EB2EB6FC80}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MADE.Media.Image", "src\MADE.Media.Image\MADE.Media.Image.csproj", "{3038EDA8-3068-45AD-99A2-36986627B880}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MADE.Networking.Tests", "tests\MADE.Networking.Tests\MADE.Networking.Tests.csproj", "{1CDB43BD-71F9-46C6-816C-4EFC9FA2ED1C}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MADE.Data.EFCore", "src\MADE.Data.EFCore\MADE.Data.EFCore.csproj", "{3A5D7EA4-5AD9-4D34-87E6-B34416CA928E}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MADE.Foundation", "src\MADE.Foundation\MADE.Foundation.csproj", "{C8DF10B0-D157-47CF-BD10-9EE1D06BEB9A}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MADE.Data.Validation.FluentValidation", "src\MADE.Data.Validation.FluentValidation\MADE.Data.Validation.FluentValidation.csproj", "{BBAB544A-BFB2-4755-8F09-8E150D3638F3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MADE.Data.Validation.FluentValidation.Tests", "tests\MADE.Data.Validation.FluentValidation.Tests\MADE.Data.Validation.FluentValidation.Tests.csproj", "{D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MADE.Data.Serialization", "src\MADE.Data.Serialization\MADE.Data.Serialization.csproj", "{0ACCC377-5FA5-47D9-B6EF-7936F1038B90}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MADE.Data.Serialization.Tests", "tests\MADE.Data.Serialization.Tests\MADE.Data.Serialization.Tests.csproj", "{7D789D04-A010-4F11-91AD-B1B94A23BAE0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Ad-Hoc|Any CPU = Ad-Hoc|Any CPU @@ -917,62 +923,6 @@ Global {CF632609-DE29-4375-B887-F0EB2EB6FC80}.Release|x64.Build.0 = Release|Any CPU {CF632609-DE29-4375-B887-F0EB2EB6FC80}.Release|x86.ActiveCfg = Release|Any CPU {CF632609-DE29-4375-B887-F0EB2EB6FC80}.Release|x86.Build.0 = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Ad-Hoc|ARM.ActiveCfg = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Ad-Hoc|ARM.Build.0 = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Ad-Hoc|ARM64.ActiveCfg = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Ad-Hoc|ARM64.Build.0 = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Ad-Hoc|x64.ActiveCfg = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Ad-Hoc|x64.Build.0 = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Ad-Hoc|x86.ActiveCfg = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Ad-Hoc|x86.Build.0 = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.AppStore|Any CPU.Build.0 = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.AppStore|ARM.ActiveCfg = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.AppStore|ARM.Build.0 = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.AppStore|ARM64.ActiveCfg = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.AppStore|ARM64.Build.0 = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.AppStore|iPhone.Build.0 = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.AppStore|x64.ActiveCfg = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.AppStore|x64.Build.0 = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.AppStore|x86.ActiveCfg = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.AppStore|x86.Build.0 = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Debug|ARM.ActiveCfg = Debug|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Debug|ARM.Build.0 = Debug|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Debug|ARM64.ActiveCfg = Debug|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Debug|ARM64.Build.0 = Debug|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Debug|iPhone.Build.0 = Debug|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Debug|x64.ActiveCfg = Debug|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Debug|x64.Build.0 = Debug|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Debug|x86.ActiveCfg = Debug|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Debug|x86.Build.0 = Debug|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Release|Any CPU.Build.0 = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Release|ARM.ActiveCfg = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Release|ARM.Build.0 = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Release|ARM64.ActiveCfg = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Release|ARM64.Build.0 = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Release|iPhone.ActiveCfg = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Release|iPhone.Build.0 = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Release|x64.ActiveCfg = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Release|x64.Build.0 = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Release|x86.ActiveCfg = Release|Any CPU - {3038EDA8-3068-45AD-99A2-36986627B880}.Release|x86.Build.0 = Release|Any CPU {1CDB43BD-71F9-46C6-816C-4EFC9FA2ED1C}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU {1CDB43BD-71F9-46C6-816C-4EFC9FA2ED1C}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU {1CDB43BD-71F9-46C6-816C-4EFC9FA2ED1C}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU @@ -1141,6 +1091,230 @@ Global {C8DF10B0-D157-47CF-BD10-9EE1D06BEB9A}.Release|x64.Build.0 = Release|Any CPU {C8DF10B0-D157-47CF-BD10-9EE1D06BEB9A}.Release|x86.ActiveCfg = Release|Any CPU {C8DF10B0-D157-47CF-BD10-9EE1D06BEB9A}.Release|x86.Build.0 = Release|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Ad-Hoc|ARM64.ActiveCfg = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Ad-Hoc|ARM64.Build.0 = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.AppStore|ARM.Build.0 = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.AppStore|ARM64.ActiveCfg = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.AppStore|ARM64.Build.0 = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.AppStore|iPhone.Build.0 = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.AppStore|x64.ActiveCfg = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.AppStore|x64.Build.0 = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.AppStore|x86.ActiveCfg = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.AppStore|x86.Build.0 = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Debug|ARM.ActiveCfg = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Debug|ARM.Build.0 = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Debug|ARM64.Build.0 = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Debug|iPhone.Build.0 = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Debug|x64.ActiveCfg = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Debug|x64.Build.0 = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Debug|x86.ActiveCfg = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Debug|x86.Build.0 = Debug|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Release|Any CPU.Build.0 = Release|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Release|ARM.ActiveCfg = Release|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Release|ARM.Build.0 = Release|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Release|ARM64.ActiveCfg = Release|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Release|ARM64.Build.0 = Release|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Release|iPhone.ActiveCfg = Release|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Release|iPhone.Build.0 = Release|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Release|x64.ActiveCfg = Release|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Release|x64.Build.0 = Release|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Release|x86.ActiveCfg = Release|Any CPU + {BBAB544A-BFB2-4755-8F09-8E150D3638F3}.Release|x86.Build.0 = Release|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Ad-Hoc|ARM64.ActiveCfg = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Ad-Hoc|ARM64.Build.0 = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.AppStore|ARM.Build.0 = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.AppStore|ARM64.ActiveCfg = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.AppStore|ARM64.Build.0 = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.AppStore|iPhone.Build.0 = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.AppStore|x64.ActiveCfg = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.AppStore|x64.Build.0 = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.AppStore|x86.ActiveCfg = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.AppStore|x86.Build.0 = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Debug|ARM.ActiveCfg = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Debug|ARM.Build.0 = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Debug|ARM64.Build.0 = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Debug|iPhone.Build.0 = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Debug|x64.ActiveCfg = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Debug|x64.Build.0 = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Debug|x86.ActiveCfg = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Debug|x86.Build.0 = Debug|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Release|Any CPU.Build.0 = Release|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Release|ARM.ActiveCfg = Release|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Release|ARM.Build.0 = Release|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Release|ARM64.ActiveCfg = Release|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Release|ARM64.Build.0 = Release|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Release|iPhone.ActiveCfg = Release|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Release|iPhone.Build.0 = Release|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Release|x64.ActiveCfg = Release|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Release|x64.Build.0 = Release|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Release|x86.ActiveCfg = Release|Any CPU + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E}.Release|x86.Build.0 = Release|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Ad-Hoc|ARM64.ActiveCfg = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Ad-Hoc|ARM64.Build.0 = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.AppStore|ARM.Build.0 = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.AppStore|ARM64.ActiveCfg = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.AppStore|ARM64.Build.0 = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.AppStore|iPhone.Build.0 = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.AppStore|x64.ActiveCfg = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.AppStore|x64.Build.0 = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.AppStore|x86.ActiveCfg = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.AppStore|x86.Build.0 = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Debug|ARM.ActiveCfg = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Debug|ARM.Build.0 = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Debug|ARM64.Build.0 = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Debug|iPhone.Build.0 = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Debug|x64.ActiveCfg = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Debug|x64.Build.0 = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Debug|x86.ActiveCfg = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Debug|x86.Build.0 = Debug|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Release|Any CPU.Build.0 = Release|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Release|ARM.ActiveCfg = Release|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Release|ARM.Build.0 = Release|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Release|ARM64.ActiveCfg = Release|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Release|ARM64.Build.0 = Release|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Release|iPhone.ActiveCfg = Release|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Release|iPhone.Build.0 = Release|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Release|x64.ActiveCfg = Release|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Release|x64.Build.0 = Release|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Release|x86.ActiveCfg = Release|Any CPU + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90}.Release|x86.Build.0 = Release|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Ad-Hoc|ARM64.ActiveCfg = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Ad-Hoc|ARM64.Build.0 = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.AppStore|ARM.Build.0 = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.AppStore|ARM64.ActiveCfg = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.AppStore|ARM64.Build.0 = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.AppStore|iPhone.Build.0 = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.AppStore|x64.ActiveCfg = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.AppStore|x64.Build.0 = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.AppStore|x86.ActiveCfg = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.AppStore|x86.Build.0 = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Debug|ARM.ActiveCfg = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Debug|ARM.Build.0 = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Debug|ARM64.Build.0 = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Debug|iPhone.Build.0 = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Debug|x64.ActiveCfg = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Debug|x64.Build.0 = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Debug|x86.ActiveCfg = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Debug|x86.Build.0 = Debug|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Release|Any CPU.Build.0 = Release|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Release|ARM.ActiveCfg = Release|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Release|ARM.Build.0 = Release|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Release|ARM64.ActiveCfg = Release|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Release|ARM64.Build.0 = Release|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Release|iPhone.ActiveCfg = Release|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Release|iPhone.Build.0 = Release|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Release|x64.ActiveCfg = Release|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Release|x64.Build.0 = Release|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Release|x86.ActiveCfg = Release|Any CPU + {7D789D04-A010-4F11-91AD-B1B94A23BAE0}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1161,10 +1335,13 @@ Global {A7EC4CC5-3800-4104-BAEF-49E8016740CF} = {01380FB8-F8A7-4416-AABA-5407574B7723} {55930DA1-35A4-445A-8028-02D22AFF20DB} = {69149D0F-BB09-411B-88F0-A1E845058D70} {CF632609-DE29-4375-B887-F0EB2EB6FC80} = {01380FB8-F8A7-4416-AABA-5407574B7723} - {3038EDA8-3068-45AD-99A2-36986627B880} = {01380FB8-F8A7-4416-AABA-5407574B7723} {1CDB43BD-71F9-46C6-816C-4EFC9FA2ED1C} = {69149D0F-BB09-411B-88F0-A1E845058D70} {3A5D7EA4-5AD9-4D34-87E6-B34416CA928E} = {01380FB8-F8A7-4416-AABA-5407574B7723} {C8DF10B0-D157-47CF-BD10-9EE1D06BEB9A} = {01380FB8-F8A7-4416-AABA-5407574B7723} + {BBAB544A-BFB2-4755-8F09-8E150D3638F3} = {01380FB8-F8A7-4416-AABA-5407574B7723} + {D8C1B3CC-B5BA-4946-944E-D898AB4DFF6E} = {69149D0F-BB09-411B-88F0-A1E845058D70} + {0ACCC377-5FA5-47D9-B6EF-7936F1038B90} = {01380FB8-F8A7-4416-AABA-5407574B7723} + {7D789D04-A010-4F11-91AD-B1B94A23BAE0} = {69149D0F-BB09-411B-88F0-A1E845058D70} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3921AD86-E6C0-4436-8880-2D9EDFAD6151} diff --git a/README.md b/README.md index 4491573d..2686b9cc 100644 --- a/README.md +++ b/README.md @@ -28,10 +28,11 @@ As many developers know, projects like MADE.NET are built and maintained in spar | Collections | [![NuGet](https://img.shields.io/nuget/v/MADE.Collections)](https://www.nuget.org/packages/MADE.Collections/) | [![Nuget](https://img.shields.io/nuget/vpre/MADE.Collections.svg)](https://www.nuget.org/packages/MADE.Collections/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/MADE.Collections.svg)](https://www.nuget.org/packages/MADE.Collections) | | Data.Converters | [![NuGet](https://img.shields.io/nuget/v/MADE.Data.Converters)](https://www.nuget.org/packages/MADE.Data.Converters/) | [![NuGet](https://img.shields.io/nuget/vpre/MADE.Data.Converters)](https://www.nuget.org/packages/MADE.Data.Converters/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/MADE.Data.Converters.svg)](https://www.nuget.org/packages/MADE.Data.Converters) | | Data.EFCore | [![NuGet](https://img.shields.io/nuget/v/MADE.Data.EFCore)](https://www.nuget.org/packages/MADE.Data.EFCore/) | [![NuGet](https://img.shields.io/nuget/vpre/MADE.Data.EFCore)](https://www.nuget.org/packages/MADE.Data.EFCore/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/MADE.Data.EFCore.svg)](https://www.nuget.org/packages/MADE.Data.EFCore) | +| Data.Serialization | [![NuGet](https://img.shields.io/nuget/v/MADE.Data.Serialization)](https://www.nuget.org/packages/MADE.Data.Serialization/) | [![NuGet](https://img.shields.io/nuget/vpre/MADE.Data.Serialization)](https://www.nuget.org/packages/MADE.Data.Serialization/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/MADE.Data.Serialization.svg)](https://www.nuget.org/packages/MADE.Data.Serialization) | | Data.Validation | [![NuGet](https://img.shields.io/nuget/v/MADE.Data.Validation)](https://www.nuget.org/packages/MADE.Data.Validation/) | [![NuGet](https://img.shields.io/nuget/vpre/MADE.Data.Validation)](https://www.nuget.org/packages/MADE.Data.Validation/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/MADE.Data.Validation.svg)](https://www.nuget.org/packages/MADE.Data.Validation) | +| Data.Validation.FluentValidation | [![NuGet](https://img.shields.io/nuget/v/MADE.Data.Validation.FluentValidation)](https://www.nuget.org/packages/MADE.Data.Validation.FluentValidation/) | [![NuGet](https://img.shields.io/nuget/vpre/MADE.Data.Validation.FluentValidation)](https://www.nuget.org/packages/MADE.Data.Validation.FluentValidation/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/MADE.Data.Validation.FluentValidation.svg)](https://www.nuget.org/packages/MADE.Data.Validation.FluentValidation) | | Diagnostics | [![NuGet](https://img.shields.io/nuget/v/MADE.Diagnostics)](https://www.nuget.org/packages/MADE.Diagnostics/) | [![NuGet](https://img.shields.io/nuget/vpre/MADE.Diagnostics)](https://www.nuget.org/packages/MADE.Diagnostics/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/MADE.Diagnostics.svg)](https://www.nuget.org/packages/MADE.Diagnostics) | | Foundation | [![NuGet](https://img.shields.io/nuget/v/MADE.Foundation)](https://www.nuget.org/packages/MADE.Foundation/) | [![NuGet](https://img.shields.io/nuget/vpre/MADE.Foundation)](https://www.nuget.org/packages/MADE.Foundation/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/MADE.Foundation.svg)](https://www.nuget.org/packages/MADE.Foundation) | -| Media.Image | [![NuGet](https://img.shields.io/nuget/v/MADE.Media.Image)](https://www.nuget.org/packages/MADE.Media.Image/) | [![NuGet](https://img.shields.io/nuget/vpre/MADE.Media.Image)](https://www.nuget.org/packages/MADE.Media.Image/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/MADE.Media.Image.svg)](https://www.nuget.org/packages/MADE.Media.Image) | | Networking | [![NuGet](https://img.shields.io/nuget/v/MADE.Networking)](https://www.nuget.org/packages/MADE.Networking/) | [![NuGet](https://img.shields.io/nuget/vpre/MADE.Networking)](https://www.nuget.org/packages/MADE.Networking/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/MADE.Networking.svg)](https://www.nuget.org/packages/MADE.Networking) | | Runtime | [![NuGet](https://img.shields.io/nuget/v/MADE.Runtime)](https://www.nuget.org/packages/MADE.Runtime/) | [![NuGet](https://img.shields.io/nuget/vpre/MADE.Runtime)](https://www.nuget.org/packages/MADE.Runtime/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/MADE.Runtime.svg)](https://www.nuget.org/packages/MADE.Runtime) | | Testing | [![NuGet](https://img.shields.io/nuget/v/MADE.Testing)](https://www.nuget.org/packages/MADE.Testing/) | [![NuGet](https://img.shields.io/nuget/vpre/MADE.Testing)](https://www.nuget.org/packages/MADE.Testing/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/MADE.Testing.svg)](https://www.nuget.org/packages/MADE.Testing) | diff --git a/docs/articles/features/data-serialization.md b/docs/articles/features/data-serialization.md new file mode 100644 index 00000000..a3e3028c --- /dev/null +++ b/docs/articles/features/data-serialization.md @@ -0,0 +1,43 @@ +--- +uid: package-data-serialization +title: Using the Data Serialization package +--- + +# Using the Data Serialization package + +The Data Serialization package provides a collection of helpers and extensions for data serialization in different types, e.g. JSON. + +## Handling type changes in JSON objects serialized with JSON.NET with TypeNameHandling set to All + +There are many ways to use JSON.NET in your applications to serialize and deserialize data. This includes the ability to set the `TypeNameHandling` property to `All` include your .NET type information within your serialized data. + +This can come with challenges when you want to use the same data in different solutions, or when you want to perform refactors or data restructures in your codebase. + +The `JsonTypeMigrationSerializationBinder` class provides a way to handle type changes in JSON objects serialized with JSON.NET, migrating from one type to another (whether known within your codebase or not). + +Here's how to setup your application for migrating JSON objects from one type to another. + +```csharp +namespace App.Migrations +{ + using MADE.Data.Serialization.Json; + using MADE.Data.Serialization.Json.Binders; + + public class JsonSerializer + { + public JsonSerializer() + { + JsonSerializerSettings.Default.TypeNameHandling = TypeNameHandling.All; + JsonSerializerSettings.Default.Binder = new JsonTypeMigrationSerializationBinder( + new JsonTypeMigration(typeof(OldType), typeof(NewType)), + new JsonTypeMigration("App.Migrations", "App.Migrations.Data.OldDataType", typeof(NewType)) + ); + } + + public T Deserialize(string serializedJson) + { + return JsonConvert.DeserializeObject(serializedJson); + } + } +} +``` diff --git a/docs/articles/features/data-validation.md b/docs/articles/features/data-validation.md index e6f924e6..188c5742 100644 --- a/docs/articles/features/data-validation.md +++ b/docs/articles/features/data-validation.md @@ -5,7 +5,7 @@ title: Using the Data Validation package # Using the Data Validation package -The Data Validation package is designed to provide out-of-the-box data validation to applications built with C#. +The Data Validation package is designed to provide out-of-the-box data validation to applications built with C#. ## Validating an object using the ValidatorCollection @@ -13,7 +13,7 @@ Data validation can be implemented in so many different ways. MADE provides the Using the `MADE.Data.Validation.ValidatorCollection` based on a `List` type, you can construct a collection of `IValidator` instances which can be used to validate values. -For example, you might want a collection of validators that ensure that a value is provided, it has a minimum length, and it contains only alphanumeric characters. +For example, you might want a collection of validators that ensure that a value is provided, it has a minimum length, and it contains only alphanumeric characters. Instead of implementing your own custom validation in your application, you can take advantage of the built-in `IValidator` implementation of this package and utilize them with the `ValidatorCollection`. @@ -69,11 +69,15 @@ If the value contains only letters, the validator will report valid; otherwise, Similar to the `AlphaValidator`, the `AlphaNumericValidator` extends the characters in the validation to include numbers also. +### Base64Validator + +The `Base64Validator` checks whether a string value is a valid Base64 encoded string. + ### BetweenValidator The `BetweenValidator` validates an `IComparable` value is between a configurable minimum and maximum range. -The range can be configured by setting the `Min` and `Max` values. +The range can be configured by setting the `Min` and `Max` values, as well as an `Inclusive` flag to configure whether the minimum and maximum values are included in the range (defaults to `true`). The in-box `System` types which implement the `IComparable` interface can be [found in the Microsoft documentation](https://docs.microsoft.com/en-us/dotnet/api/system.icomparable?view=net-5.0). @@ -89,6 +93,12 @@ this.Pattern = @"^(?!\.)(""([^""\r\\]|\\[""\r\\])*""|" + @"([-a-zA-Z0-9!#$%&'*+/ MADE.NET has a comprehensive set of test cases which validate the implementation with a variety of different valid and invalid email addresses. +### GuidValidator + +The `GuidValidator` checks whether a string value is a valid GUID. + +The underlying implementation uses the `Guid.TryParse` method to validate the string. + ### IpAddressValidator The `IpAddressValidator` is a simple data validator which ensures that a value is a valid IP address. @@ -100,9 +110,25 @@ The implementation splits the IP address into each nibble and validates them bas - Is a digit - Is a numeric value between 0 and 255 +### LatitudeValidator + +The `LatitudeValidator` validates a value is within the valid range for a latitude value (-90 and 90). + +### LongitudeValidator + +The `LongitudeValidator` validates a value is within the valid range for a longitude value (-180 and 180). + +### MacAddressValidator + +The `MacAddressValidator` is a simple data validator which ensures that a value is a valid MAC address. + +The implementation uses the .NET `PhysicalAddress` class to parse the provided value. + +For more information on the `PhysicalAddress` class, see the [Microsoft documentation](https://docs.microsoft.com/en-us/dotnet/api/system.net.networkinformation.physicaladdress?view=net-6.0). + ### MaxValueValidator -The `MaxValueValidator` validates an `IComparable` value is less than a configurable maximum value. +The `MaxValueValidator` validates an `IComparable` value is less than a configurable maximum value. The maximum can be configured by setting the `Max` value. @@ -110,12 +136,16 @@ The in-box `System` types which implement the `IComparable` interface can be [fo ### MinValueValidator -The `MinValueValidator` validates an `IComparable` value is greater than a configurable minimum value. +The `MinValueValidator` validates an `IComparable` value is greater than a configurable minimum value. The minimum can be configured by setting the `Min` value. The in-box `System` types which implement the `IComparable` interface can be [found in the Microsoft documentation](https://docs.microsoft.com/en-us/dotnet/api/system.icomparable?view=net-5.0). +### PredicateValidator + +The `PredicateValidator` validates a value using a custom predicate to ensure that a condition is met. + ### RegexValidator The `RegexValidator` is a generic data validator which validates a value based on a configurable regular expression pattern. @@ -191,3 +221,13 @@ namespace MADE.Data.Validation.Validators ``` If there is a common data validator you think is missing from MADE.NET, [raise a tracking item on GitHub](https://github.com/MADE-Apps/MADE.NET/issues/new/choose) and we'll get it implemented. + +## Using FluentValidation with MADE.NET + +The `MADE.Data.Validation.FluentValidation` package provides an easy way to take advantage of validation with the [FluentValidation](https://fluentvalidation.net/) library validator framework. + +### Validating an object using the FluentValidatorCollection + +Using the `MADE.Data.Validation.FluentValidatorCollection` based on a `List` type, you can construct a collection of `AbstractValidator` instances which can be used to validate values. + +This way, you can bring FluentValidation's out-of-the-box validators or your own custom validators based on the `AbstractValidator` type and get all the benefits of using the existing MADE.NET validation framework. This is great for example with input validator controls that currently support the MADE.NET validation framework! diff --git a/docs/articles/features/media-image.md b/docs/articles/features/media-image.md deleted file mode 100644 index 2ee22f89..00000000 --- a/docs/articles/features/media-image.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -uid: package-media-image -title: Using the Media Image package ---- - -# Using the Media Image package - -The Media Image package is designed to be used in applications that require image processing. - -## Loading Windows StorageFile thumbnails into an Image with LoadStorageFileThumbnailImageBehavior - -The `MADE.Media.Image.Behaviors.LoadStorageFileThumbnailImageBehavior` is a custom behavior built on the [Microsoft XAML behaviors SDK](https://github.com/Microsoft/XamlBehaviors). - -It can be attached to an `Image` UI element and used to load the thumbnail of a `StorageFile`. - -You can do this in your Windows XAML as shown below. - -```xml - - - - - - - - - - - -``` - -This could result in a generated UI that looks like this. - -Result of using LoadStorageFileThumbnailImageBehavior diff --git a/docs/articles/intro.md b/docs/articles/intro.md index 78ae7879..1cd3688c 100644 --- a/docs/articles/intro.md +++ b/docs/articles/intro.md @@ -28,9 +28,11 @@ dotnet add package MADE.Collections | MADE.Collections | [![NuGet](https://img.shields.io/nuget/v/MADE.Collections)](https://www.nuget.org/packages/MADE.Collections/) | | MADE.Data.Converters | [![NuGet](https://img.shields.io/nuget/v/MADE.Data.Converters)](https://www.nuget.org/packages/MADE.Data.Converters/) | | MADE.Data.EFCore | [![NuGet](https://img.shields.io/nuget/v/MADE.Data.EFCore)](https://www.nuget.org/packages/MADE.Data.EFCore/) | +| MADE.Data.Serialization | [![NuGet](https://img.shields.io/nuget/v/MADE.Data.Serialization)](https://www.nuget.org/packages/MADE.Data.Serialization/) | | MADE.Data.Validation | [![NuGet](https://img.shields.io/nuget/v/MADE.Data.Validation)](https://www.nuget.org/packages/MADE.Data.Validation/) | +| MADE.Data.Validation.FluentValidation | [![NuGet](https://img.shields.io/nuget/v/MADE.Data.Validation.FluentValidation)](https://www.nuget.org/packages/MADE.Data.Validation.FluentValidation/) | | MADE.Diagnostics | [![NuGet](https://img.shields.io/nuget/v/MADE.Diagnostics)](https://www.nuget.org/packages/MADE.Diagnostics/) | -| MADE.Media.Image | [![NuGet](https://img.shields.io/nuget/v/MADE.Media.Image)](https://www.nuget.org/packages/MADE.Media.Image/) | +| MADE.Foundation | [![NuGet](https://img.shields.io/nuget/v/MADE.Foundation)](https://www.nuget.org/packages/MADE.Foundation/) | | MADE.Networking | [![NuGet](https://img.shields.io/nuget/v/MADE.Networking)](https://www.nuget.org/packages/MADE.Networking/) | | MADE.Runtime | [![NuGet](https://img.shields.io/nuget/v/MADE.Runtime)](https://www.nuget.org/packages/MADE.Runtime/) | | MADE.Testing | [![NuGet](https://img.shields.io/nuget/v/MADE.Testing)](https://www.nuget.org/packages/MADE.Testing/) | @@ -87,6 +89,20 @@ It includes features such as: +#### Data.Serialization + +The Data Serialization package provides a collection of helpers and extensions for data serialization in different types, e.g. JSON. + +It includes features such as: + +- JsonTypeMigrationSerializationBinder, for migrating type names within a serialized JSON object. + + + +[Discover Data.Serialization](features/data-serialization.md) + + + #### Data.Validation The Data Validation package is designed to provide out-of-the-box data validation to applications built with C#. @@ -125,20 +141,6 @@ It includes features such as: -#### Media.Image - -The Media Image package is designed to be used in applications that require image processing. - -It provides capabilities, such as: - -- LoadStorageFileThumbnailImageBehavior, a UWP XAML behavior for loading a thumbnail from a `StorageFile` on an `Image` element. - - - -[Discover Media.Image](features/media-image.md) - - - #### Networking The Networking package contains a collection of helpers for applications that use `HttpClient` for making network requests to APIs. diff --git a/docs/articles/toc.yml b/docs/articles/toc.yml index 2a4e3143..39300c62 100644 --- a/docs/articles/toc.yml +++ b/docs/articles/toc.yml @@ -8,6 +8,8 @@ href: features/data-converters.md - name: Data EF Core href: features/data-efcore.md + - name: Data Serialization + href: features/data-serialization.md - name: Data Validation href: features/data-validation.md - name: Diagnostics @@ -25,4 +27,4 @@ - name: Web href: features/web.md - name: Web MVC - href: features/web-mvc.md \ No newline at end of file + href: features/web-mvc.md diff --git a/docs/index.md b/docs/index.md index 604f45b1..77c5df63 100644 --- a/docs/index.md +++ b/docs/index.md @@ -106,7 +106,7 @@ title: Make App Development Easier
- v1.4.0 + v1.5.0
@@ -122,7 +122,7 @@ title: Make App Development Easier
- 225k+ + 300k+
@@ -138,7 +138,7 @@ title: Make App Development Easier
- 22 + 27
diff --git a/src/MADE.Collections/CollectionExtensions.cs b/src/MADE.Collections/CollectionExtensions.cs index 18048c7f..07b5f453 100644 --- a/src/MADE.Collections/CollectionExtensions.cs +++ b/src/MADE.Collections/CollectionExtensions.cs @@ -5,6 +5,7 @@ namespace MADE.Collections { using System; using System.Collections.Generic; + using System.Collections.ObjectModel; using System.Linq; /// @@ -396,5 +397,64 @@ public static IEnumerable Shuffle(this IEnumerable source) { return source.OrderBy(x => Guid.NewGuid()); } + + /// Sorts the elements in the entire using the specified comparer. + /// The source collection to sort. + /// The implementation to use when comparing elements. + /// The type of item in the collection. + /// The key value of the item to sort on. + public static void Sort(this ObservableCollection source, Func comparer) + { + if (source is not { Count: > 1 }) + { + return; + } + + var idx = 0; + foreach (var originalIdx in source.OrderBy(comparer).Select(source.IndexOf)) + { + if (originalIdx != idx) + { + source.Move(originalIdx, idx); + } + + idx++; + } + } + + /// Sorts the elements in the entire using the specified comparer in descending order. + /// The source collection to sort. + /// The implementation to use when comparing elements. + /// The type of item in the collection. + /// The key value of the item to sort on. + public static void SortDescending(this ObservableCollection source, Func comparer) + { + if (source is not { Count: > 1 }) + { + return; + } + + var idx = 0; + foreach (var originalIdx in source.OrderByDescending(comparer).Select(source.IndexOf)) + { + if (originalIdx != idx) + { + source.Move(originalIdx, idx); + } + + idx++; + } + } + + /// Indicates whether the specified collection is or empty (containing no items). + /// The collection to test. + /// The type of item in the collection. + /// + /// if the parameter is or empty (containing no items); otherwise, . + /// + public static bool IsNullOrEmpty(this IEnumerable source) + { + return source is null || !source.Any(); + } } } \ No newline at end of file diff --git a/src/MADE.Collections/DictionaryExtensions.cs b/src/MADE.Collections/DictionaryExtensions.cs index 7b4471d9..3d4fab85 100644 --- a/src/MADE.Collections/DictionaryExtensions.cs +++ b/src/MADE.Collections/DictionaryExtensions.cs @@ -49,5 +49,29 @@ public static void AddOrUpdate(this Dictionary dicti dictionary.Add(key, value); } + + /// + /// Gets a value from a dictionary by the specified key, or returns a default value. + /// + /// The type of key item within the dictionary. + /// The type of value item within the dictionary. + /// The dictionary to get a value from. + /// The key to get a value for. + /// The default value to return if not exists. Default, null. + /// The value if it exists for the key; otherwise, null. + public static TValue GetValueOrDefault( + this Dictionary dictionary, + TKey key, + TValue defaultValue = default) + { + var result = defaultValue; + + if (dictionary != null && dictionary.ContainsKey(key)) + { + result = dictionary[key]; + } + + return result; + } } } \ No newline at end of file diff --git a/src/MADE.Collections/MADE.Collections.csproj b/src/MADE.Collections/MADE.Collections.csproj index bd83a88b..1c1dc1a7 100644 --- a/src/MADE.Collections/MADE.Collections.csproj +++ b/src/MADE.Collections/MADE.Collections.csproj @@ -1,7 +1,7 @@ - uap10.0.17763;netstandard2.0 + netstandard2.0 true MADE.NET Collections diff --git a/src/MADE.Data.Converters/BooleanToStringValueConverter.Windows.cs b/src/MADE.Data.Converters/BooleanToStringValueConverter.Windows.cs index eb926d81..303783d4 100644 --- a/src/MADE.Data.Converters/BooleanToStringValueConverter.Windows.cs +++ b/src/MADE.Data.Converters/BooleanToStringValueConverter.Windows.cs @@ -5,7 +5,6 @@ namespace MADE.Data.Converters { using System; - using MADE.Data.Converters.Exceptions; using MADE.Data.Converters.Strings; using Windows.UI.Xaml; using Windows.UI.Xaml.Data; @@ -13,7 +12,8 @@ namespace MADE.Data.Converters /// /// Defines a Windows components for a XAML value converter from to . /// - public class BooleanToStringValueConverter : DependencyObject, IValueConverter, IValueConverter + [Obsolete("BooleanToStringValueConverter for Windows will be removed in a future major release. Use the replicated BooleanToStringValueConverter type from the MADE.UI.Data.Converters library instead.")] + public partial class BooleanToStringValueConverter : DependencyObject, IValueConverter, IValueConverter { /// /// Defines the dependency property for . @@ -87,50 +87,6 @@ public object ConvertBack(object value, Type targetType, object parameter, strin return this.ConvertBack(b, parameter); } - - /// - /// Converts the value to the type. - /// - /// - /// The value to convert. - /// - /// - /// The optional parameter used to help with conversion. - /// - /// - /// The converted object. - /// - public string Convert(bool value, object parameter = default) - { - return value ? this.TrueValue : this.FalseValue; - } - - /// - /// Converts the value back to the type. - /// - /// - /// The value to convert. - /// - /// - /// The optional parameter used to help with conversion. - /// - /// - /// The converted object. - /// - public bool ConvertBack(string value, object parameter = default) - { - if (value == this.TrueValue) - { - return true; - } - - if (value == this.FalseValue) - { - return false; - } - - throw new InvalidDataConversionException(nameof(BooleanToStringValueConverter), value, $"The value to convert back is not of the expected {nameof(this.TrueValue)} or {nameof(this.FalseValue)}"); - } } } #endif \ No newline at end of file diff --git a/src/MADE.Data.Converters/BooleanToStringValueConverter.cs b/src/MADE.Data.Converters/BooleanToStringValueConverter.cs new file mode 100644 index 00000000..147e140a --- /dev/null +++ b/src/MADE.Data.Converters/BooleanToStringValueConverter.cs @@ -0,0 +1,67 @@ +namespace MADE.Data.Converters +{ + using MADE.Data.Converters.Exceptions; + using MADE.Data.Converters.Extensions; + + /// + /// Defines a value converter from to with a pre-determined and . + /// + public partial class BooleanToStringValueConverter : IValueConverter + { +#if !WINDOWS_UWP + /// + /// Gets or sets the positive/true value. + /// + public string TrueValue { get; set; } + + /// + /// Gets or sets the negative/false value. + /// + public string FalseValue { get; set; } +#endif + + /// + /// Converts the value to the type. + /// + /// + /// The value to convert. + /// + /// + /// The optional parameter used to help with conversion. + /// + /// + /// The converted object. + /// + public string Convert(bool value, object parameter = default) + { + return value.ToFormattedString(this.TrueValue, this.FalseValue); + } + + /// + /// Converts the value back to the type. + /// + /// + /// The value to convert. + /// + /// + /// The optional parameter used to help with conversion. + /// + /// + /// The converted object. + /// + public bool ConvertBack(string value, object parameter = default) + { + if (value == this.TrueValue) + { + return true; + } + + if (value == this.FalseValue) + { + return false; + } + + throw new InvalidDataConversionException(nameof(BooleanToStringValueConverter), value, $"The value to convert back is not of the expected {nameof(this.TrueValue)} or {nameof(this.FalseValue)}"); + } + } +} diff --git a/src/MADE.Data.Converters/DateTimeToStringValueConverter.Windows.cs b/src/MADE.Data.Converters/DateTimeToStringValueConverter.Windows.cs index 27af2690..2f3ac5f4 100644 --- a/src/MADE.Data.Converters/DateTimeToStringValueConverter.Windows.cs +++ b/src/MADE.Data.Converters/DateTimeToStringValueConverter.Windows.cs @@ -10,6 +10,7 @@ namespace MADE.Data.Converters /// /// Defines a Windows components for a XAML value converter from to with an optional format string. /// + [Obsolete("DateTimeToStringValueConverter for Windows will be removed in a future major release. Use the replicated DateTimeToStringValueConverter type from the MADE.UI.Data.Converters library instead.")] public partial class DateTimeToStringValueConverter : IValueConverter { /// diff --git a/src/MADE.Data.Converters/Extensions/BooleanExtensions.cs b/src/MADE.Data.Converters/Extensions/BooleanExtensions.cs new file mode 100644 index 00000000..e9c04f89 --- /dev/null +++ b/src/MADE.Data.Converters/Extensions/BooleanExtensions.cs @@ -0,0 +1,40 @@ +// MADE Apps licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace MADE.Data.Converters.Extensions +{ + /// + /// Defines a collection of extensions for values. + /// + public static class BooleanExtensions + { + /// + /// Converts a value to a value with optional true/false values. + /// + /// The value to format. + /// The format for when the is true. + /// The format for when the is false. + /// A formatted string + public static string ToFormattedString(this bool value, string trueValue = "True", string falseValue = "False") + { + return value ? trueValue : falseValue; + } + + /// + /// Converts a nullable value to a value with optional true/false/null values. + /// + /// The value to format. + /// The format for when the is true. Default, True. + /// The format for when the is false. Default, False. + /// The format for when the is null. Default, Not set. + /// A formatted string + public static string ToFormattedString( + this bool? value, + string trueValue = "True", + string falseValue = "False", + string nullValue = "Not set") + { + return value.HasValue ? value.Value ? trueValue : falseValue : nullValue; + } + } +} \ No newline at end of file diff --git a/src/MADE.Data.Converters/Extensions/CollectionExtensions.cs b/src/MADE.Data.Converters/Extensions/CollectionExtensions.cs new file mode 100644 index 00000000..25263859 --- /dev/null +++ b/src/MADE.Data.Converters/Extensions/CollectionExtensions.cs @@ -0,0 +1,25 @@ +// MADE Apps licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace MADE.Data.Converters.Extensions +{ + using System.Collections.Generic; + + /// + /// Defines a collection of extensions for collection objects. + /// + public static class CollectionExtensions + { + /// + /// Converts a collection of items to a string separated by a delimiter. + /// + /// The type of item within the collection. + /// The source collection to convert. + /// The delimiter to separate items by in the string. Default, comma. + /// A delimited string representing the collection. + public static string ToDelimitedString(this IEnumerable source, string delimiter = ",") + { + return string.Join(delimiter, source); + } + } +} \ No newline at end of file diff --git a/src/MADE.Data.Converters/Extensions/LengthExtensions.cs b/src/MADE.Data.Converters/Extensions/LengthExtensions.cs new file mode 100644 index 00000000..282941ea --- /dev/null +++ b/src/MADE.Data.Converters/Extensions/LengthExtensions.cs @@ -0,0 +1,28 @@ +namespace MADE.Data.Converters.Extensions +{ + /// + /// Defines a collection of extensions for converting length measurements. + /// + public static class LengthExtensions + { + /// + /// Converts a distance measured in miles to a distance measured in meters. + /// + /// The miles to convert to meters. + /// The meters that represent the miles. + public static double ToMeters(this double miles) + { + return miles * 1609.344; + } + + /// + /// Converts a distance measured in meters to a distance measured in miles. + /// + /// The meters to convert to miles. + /// The miles that represent the meters. + public static double ToMiles(this double meters) + { + return meters / 1609.344; + } + } +} \ No newline at end of file diff --git a/src/MADE.Data.EFCore/Extensions/QueryableExtensions.cs b/src/MADE.Data.EFCore/Extensions/QueryableExtensions.cs new file mode 100644 index 00000000..3a520704 --- /dev/null +++ b/src/MADE.Data.EFCore/Extensions/QueryableExtensions.cs @@ -0,0 +1,40 @@ +namespace MADE.Data.EFCore.Extensions +{ + using System; + using System.Linq; + using Z.EntityFramework.Plus; + + /// + /// Defines a collection of extensions for Entity Framework queries. + /// + public static class QueryableExtensions + { + /// + /// Skips and takes a subset of a data query based on the specified current page and page size requested. + /// + /// The type of entity being queried. + /// The current query. + /// The current page being requested. + /// The size of the page being requested. + /// The paginated query. + public static IQueryable Page(this IQueryable query, int page, int pageSize) + { + return query.Skip((page - 1) * pageSize).Take(pageSize); + } + + /// + /// Orders the query results by the specified property name from the entity with the option for order by ascending or descending. + /// + /// The type of entity being ordered. + /// The query to order. + /// The property/column name to sort on for the entity. + /// A value indicating whether to sort descending. + /// The ordered query. + public static IQueryable OrderBy(this IQueryable query, string sortName, bool sortDesc) + { + return string.IsNullOrWhiteSpace(sortName) + ? query + : (!sortDesc ? query.AddOrAppendOrderBy(sortName) : query.AddOrAppendOrderByDescending(sortName)); + } + } +} \ No newline at end of file diff --git a/src/MADE.Data.EFCore/MADE.Data.EFCore.csproj b/src/MADE.Data.EFCore/MADE.Data.EFCore.csproj index 5359b7a6..baff6b1d 100644 --- a/src/MADE.Data.EFCore/MADE.Data.EFCore.csproj +++ b/src/MADE.Data.EFCore/MADE.Data.EFCore.csproj @@ -9,17 +9,20 @@ - DbContextExtensions for extending DbContext objects with helper methods, such as updating an item asynchronously and removing items matching a specified criteria. - EntityBase for providing a base definition for an entity within a DbContext, including a primary key, created and updated dates. - UtcDateTimeConverter for ensuring that DateTime values are stored and retrieving in UTC format. + - QueryableExtensions for extending IQueryable objects with helper methods, such as filtering, ordering, and paging. MADE EFCore EntityFramework - + + - + + diff --git a/src/MADE.Data.Serialization/Json/Binders/JsonTypeMigrationSerializationBinder.cs b/src/MADE.Data.Serialization/Json/Binders/JsonTypeMigrationSerializationBinder.cs new file mode 100644 index 00000000..97fbe236 --- /dev/null +++ b/src/MADE.Data.Serialization/Json/Binders/JsonTypeMigrationSerializationBinder.cs @@ -0,0 +1,114 @@ +namespace MADE.Data.Serialization.Json.Binders +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading; + using System.Threading.Tasks; + using MADE.Data.Serialization.Json; + using MADE.Data.Serialization.Json.Exceptions; + using Newtonsoft.Json.Serialization; + + /// + /// Defines a serialization binder for JSON.NET for migrating serialized declarations within a serialized JSON object. + /// + /// + /// This is for migrating serialized types where TypeNameHandling.All has been set in the JSON serializer settings. + /// + public class JsonTypeMigrationSerializationBinder : DefaultSerializationBinder + { + private readonly SemaphoreSlim migrationSemaphore; + + private readonly List migrations = new(); + + /// + /// Initializes a new instance of the class. + /// + /// + /// To add migrations, call the method. + /// + public JsonTypeMigrationSerializationBinder() + : this(null) + { + } + + /// + /// Initializes a new instance of the class with pre-configured type migrations. + /// + /// The type migrations to initialize with. + public JsonTypeMigrationSerializationBinder(params JsonTypeMigration[] migrations) + { + this.migrationSemaphore = new SemaphoreSlim(1, 1); + + if (migrations != null && migrations.Any()) + { + this.migrations.AddRange(migrations); + } + } + + /// + /// Adds a JSON type migration to the binder. + /// + /// The type migration to add. + /// An asynchronous operation. + /// Thrown if the is null. + /// Thrown if a already exists for the from type. + public async Task AddTypeMigrationAsync(JsonTypeMigration migration) + { + if (migration == null) + { + throw new ArgumentNullException(nameof(migration)); + } + + await this.migrationSemaphore.WaitAsync(); + + try + { + JsonTypeMigration existingMigration = this.migrations.FirstOrDefault( + m => + m.FromAssemblyName == migration.FromAssemblyName && + m.FromTypeName == migration.FromTypeName); + + if (existingMigration != null) + { + throw new JsonTypeMigrationException( + $"A type migration is already registered for type {existingMigration.FromTypeName} in assembly {existingMigration.FromAssemblyName} to {existingMigration.ToType.FullName}"); + } + + this.migrations.Add(migration); + } + finally + { + this.migrationSemaphore.Release(); + } + } + + /// + /// When overridden in a derived class, controls the binding of a serialized object to a type. + /// + /// Specifies the name of the serialized object. + /// Specifies the name of the serialized object. + /// + /// The type of the object the formatter creates a new instance of. + /// + public override Type BindToType(string assemblyName, string typeName) + { + Task task = this.migrationSemaphore.WaitAsync(); + Task.WaitAll(task); + + JsonTypeMigration migration = null; + try + { + migration = this.migrations.FirstOrDefault( + m => + m.FromAssemblyName == assemblyName && m.FromTypeName == typeName); + } + finally + { + this.migrationSemaphore.Release(); + } + + return migration != null ? migration.ToType : base.BindToType(assemblyName, typeName); + } + } +} \ No newline at end of file diff --git a/src/MADE.Data.Serialization/Json/Exceptions/JsonTypeMigrationException.cs b/src/MADE.Data.Serialization/Json/Exceptions/JsonTypeMigrationException.cs new file mode 100644 index 00000000..0bb046b3 --- /dev/null +++ b/src/MADE.Data.Serialization/Json/Exceptions/JsonTypeMigrationException.cs @@ -0,0 +1,36 @@ +namespace MADE.Data.Serialization.Json.Exceptions +{ + using System; + + /// + /// Defines an exception for errors occurred when interacting with JSON type migrations. + /// + public class JsonTypeMigrationException : Exception + { + /// + /// Initializes a new instance of the class. + /// + public JsonTypeMigrationException() + { + } + + /// + /// Initializes a new instance of the class with a message that describes the error. + /// + /// The message that describes the error. + public JsonTypeMigrationException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class with a message that describes the error and inner exception. + /// + /// The message that describes the error. + /// The exception that caused this exception to be thrown. + public JsonTypeMigrationException(string message, Exception innerException) + : base(message, innerException) + { + } + } +} \ No newline at end of file diff --git a/src/MADE.Data.Serialization/Json/JsonTypeMigration.cs b/src/MADE.Data.Serialization/Json/JsonTypeMigration.cs new file mode 100644 index 00000000..15a6fa21 --- /dev/null +++ b/src/MADE.Data.Serialization/Json/JsonTypeMigration.cs @@ -0,0 +1,51 @@ +namespace MADE.Data.Serialization.Json +{ + using System; + using MADE.Data.Serialization.Json.Binders; + + /// + /// Defines the detail for migrating from one type to another using the . + /// + public class JsonTypeMigration + { + /// + /// Initializes a new instance of the class with the expected from and to migration types. + /// + /// The type being migrated from. + /// The type being migrated to. + public JsonTypeMigration(Type fromType, Type toType) + { + this.FromAssemblyName = fromType.Assembly.GetName().Name; + this.FromTypeName = fromType.FullName; + this.ToType = toType; + } + + /// + /// Initializes a new instance of the class with the expected from and to migration types. + /// + /// The name of the assembly being migrated from. + /// The name of the type being migrated from. + /// The type being migrated to. + public JsonTypeMigration(string fromAssemblyName, string fromTypeName, Type toType) + { + this.FromAssemblyName = fromAssemblyName; + this.FromTypeName = fromTypeName; + this.ToType = toType; + } + + /// + /// Gets the name of the assembly being migrated from. + /// + public string FromAssemblyName { get; } + + /// + /// Gets the name of the type being migrated from. + /// + public string FromTypeName { get; } + + /// + /// Gets the type being migrated to. + /// + public Type ToType { get; } + } +} \ No newline at end of file diff --git a/src/MADE.Data.Serialization/MADE.Data.Serialization.csproj b/src/MADE.Data.Serialization/MADE.Data.Serialization.csproj new file mode 100644 index 00000000..d9bc7cbf --- /dev/null +++ b/src/MADE.Data.Serialization/MADE.Data.Serialization.csproj @@ -0,0 +1,18 @@ + + + + netstandard2.0 + true + MADE.NET Data Serialization + + This package includes: + - JsonTypeMigrationSerializationBinder for migrating type names within a serialized JSON object. + + MADE Data Serialization JSON + + + + + + + \ No newline at end of file diff --git a/src/MADE.Data.Validation.FluentValidation/FluentValidatorCollection{T}.cs b/src/MADE.Data.Validation.FluentValidation/FluentValidatorCollection{T}.cs new file mode 100644 index 00000000..2b7485cb --- /dev/null +++ b/src/MADE.Data.Validation.FluentValidation/FluentValidatorCollection{T}.cs @@ -0,0 +1,91 @@ +// MADE Apps licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace MADE.Data.Validation +{ + using System; + using System.Collections.Generic; + using FluentValidation; + + /// + /// Defines a list of objects that can be accessed by index. + /// + /// The type of item being validated. + public class FluentValidatorCollection : List>, IValidatorCollection + { + private readonly List feedbackMessages = new(); + + /// Initializes a new instance of the class that is empty and has the default initial capacity. + public FluentValidatorCollection() + { + } + + /// Initializes a new instance of the class that contains elements copied from the specified collection and has sufficient capacity to accommodate the number of elements copied. + /// The collection whose elements are copied to the new list. + /// collection is null. + public FluentValidatorCollection(IEnumerable> collection) + : base(collection) + { + } + + /// + /// Initializes a new instance of the class that is empty and has the specified initial capacity. + /// The number of elements that the new list can initially store. + /// capacity is less than 0. + public FluentValidatorCollection(int capacity) + : base(capacity) + { + } + + /// + /// Occurs when the input value is validated against the collection of validators. + /// + public event InputValidatedEventHandler Validated; + + /// + /// Gets or sets a value indicating whether the data provided is in an invalid state. + /// + public bool IsInvalid { get; set; } + + /// + /// Gets or sets a value indicating whether the data is dirty. + /// + public bool IsDirty { get; set; } + + /// + /// Gets the validator feedback messages for ones which are invalid. + /// + public IEnumerable FeedbackMessages => this.feedbackMessages; + + /// + /// Executes data validation on the provided against the validators provided. + /// + /// The value to be validated. + /// Potentially thrown by the delegate callback. + public void Validate(object value) + { + this.feedbackMessages.Clear(); + + this.IsDirty = true; + + this.ForEach(validator => + { + var result = validator.Validate((T)value); + if (!result.IsValid) + { + IsInvalid = true; + } + + if (result.Errors != null) + { + foreach (var message in result.Errors) + { + this.feedbackMessages.Add(message.ErrorMessage); + } + } + }); + + this.Validated?.Invoke(this, new InputValidatedEventArgs(this.IsInvalid, this.IsDirty)); + } + } +} \ No newline at end of file diff --git a/src/MADE.Data.Validation.FluentValidation/MADE.Data.Validation.FluentValidation.csproj b/src/MADE.Data.Validation.FluentValidation/MADE.Data.Validation.FluentValidation.csproj new file mode 100644 index 00000000..a6a8fb1e --- /dev/null +++ b/src/MADE.Data.Validation.FluentValidation/MADE.Data.Validation.FluentValidation.csproj @@ -0,0 +1,22 @@ + + + + netstandard2.0 + true + MADE.NET Data Validation (FluentValidation) + + This package includes extended support for FluentValidation validators in MADE.NET. + + MADE Data Validation FluentValidation + MADE.Data.Validation + + + + + + + + + + + \ No newline at end of file diff --git a/src/MADE.Data.Validation/IValidatorCollection.cs b/src/MADE.Data.Validation/IValidatorCollection.cs new file mode 100644 index 00000000..77ce3c75 --- /dev/null +++ b/src/MADE.Data.Validation/IValidatorCollection.cs @@ -0,0 +1,35 @@ +// MADE Apps licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace MADE.Data.Validation +{ + using System.Collections; + using System.Collections.Generic; + + /// + /// Defines an interface for a collection of validators. + /// + public interface IValidatorCollection : IList + { + /// + /// Gets or sets a value indicating whether the data provided is in an invalid state. + /// + bool IsInvalid { get; set; } + + /// + /// Gets or sets a value indicating whether the data is dirty. + /// + bool IsDirty { get; set; } + + /// + /// Gets the validator feedback messages for ones which are invalid. + /// + IEnumerable FeedbackMessages { get; } + + /// + /// Executes data validation on the provided against the validators provided. + /// + /// The value to be validated. + void Validate(object value); + } +} \ No newline at end of file diff --git a/src/MADE.Data.Validation/MADE.Data.Validation.csproj b/src/MADE.Data.Validation/MADE.Data.Validation.csproj index 6c7441e6..fdc167d1 100644 --- a/src/MADE.Data.Validation/MADE.Data.Validation.csproj +++ b/src/MADE.Data.Validation/MADE.Data.Validation.csproj @@ -1,22 +1,29 @@ - uap10.0.17763;netstandard2.0 + netstandard2.0 true MADE.NET Data Validation This package includes: - - AlphaNumericValidator for validating whether a string contains only alphanumeric characters. - - AlphaValidator for validating whether a string contains only alpha characters. - - BetweenValidator for validating whether a value is within a minimum and maximum range. - - EmailValidator for validating whether a value is an email address. - - IpAddressValidator for validating whether a value is a valid IP address. - - MaxLengthValidator for validating whether a value is below a maximum length. - - MaxValueValidator for validating whether a value is below a maximum value. - - MinLengthValidator for validating whether a value is above a minimum length. - - MinValueValidator for validating whether a value is above a minimum value. - - RegexValidator for validating whether a value matches a regular expression. - - RequiredValidator for validating whether a value has been provided. + - AlphaNumericValidator for ensuring a string contains only alphanumeric characters. + - AlphaValidator for ensuring a string contains only alpha characters. + - Base64Validator for ensuring a string is a valid base64 string. + - BetweenValidator for ensuring a value is within a minimum and maximum range. + - EmailValidator for ensuring a value is an email address. + - GuidValidator for ensuring a value is a GUID. + - IpAddressValidator for ensuring a value is a valid IP address. + - LatitudeValidator for ensuring a value is a valid latitude. + - LongitudeValidator for ensuring a value is a valid longitude. + - MacAddressValidator for ensuring a value is a valid MAC address. + - MaxLengthValidator for ensuring a value is below a maximum length. + - MaxValueValidator for ensuring a value is below a maximum value. + - MinLengthValidator for ensuring a value is above a minimum length. + - MinValueValidator for ensuring a value is above a minimum value. + - PredicateValidator for ensuring a value meets the condition of a value predicate. + - RegexValidator for ensuring a value matches a regular expression. + - RequiredValidator for ensuring a value has been provided. + - WellFormedUrlValidator for ensuring a value is a well-formed URL. MADE Data Validation RegEx Range Email IpAddress Min Max Required Length diff --git a/src/MADE.Data.Validation/Strings/Resources.Designer.cs b/src/MADE.Data.Validation/Strings/Resources.Designer.cs index 7740cc91..24ea2ea2 100644 --- a/src/MADE.Data.Validation/Strings/Resources.Designer.cs +++ b/src/MADE.Data.Validation/Strings/Resources.Designer.cs @@ -10,7 +10,6 @@ namespace MADE.Data.Validation.Strings { using System; - using System.Reflection; /// @@ -20,7 +19,7 @@ namespace MADE.Data.Validation.Strings { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class Resources { @@ -40,7 +39,7 @@ internal Resources() { public static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MADE.Data.Validation.Strings.Resources", typeof(Resources).GetTypeInfo().Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MADE.Data.Validation.Strings.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; @@ -79,6 +78,15 @@ public static string AlphaValidator_FeedbackMessage { } } + /// + /// Looks up a localized string similar to The value must be a valid base64 string.. + /// + public static string Base64Validator_FeedbackMessage { + get { + return ResourceManager.GetString("Base64Validator_FeedbackMessage", resourceCulture); + } + } + /// /// Looks up a localized string similar to The value must be between {0} and {1}.. /// @@ -97,6 +105,15 @@ public static string EmailValidator_FeedbackMessage { } } + /// + /// Looks up a localized string similar to The value must be a valid GUID.. + /// + public static string GuidValidator_FeedbackMessage { + get { + return ResourceManager.GetString("GuidValidator_FeedbackMessage", resourceCulture); + } + } + /// /// Looks up a localized string similar to The value must be a valid IP address, e.g. 192.168.0.1.. /// @@ -106,6 +123,15 @@ public static string IpAddressValidator_FeedbackMessage { } } + /// + /// Looks up a localized string similar to The value must be a valid MAC address, e.g. 00:11:22:33:44:55.. + /// + public static string MacAddressValidator_FeedbackMessage { + get { + return ResourceManager.GetString("MacAddressValidator_FeedbackMessage", resourceCulture); + } + } + /// /// Looks up a localized string similar to The length must be less than {0}.. /// @@ -142,6 +168,15 @@ public static string MinValueValidator_FeedbackMessage { } } + /// + /// Looks up a localized string similar to The value is not valid.. + /// + public static string PredicateValidator_FeedbackMessage { + get { + return ResourceManager.GetString("PredicateValidator_FeedbackMessage", resourceCulture); + } + } + /// /// Looks up a localized string similar to The value does not match the valid mask.. /// @@ -159,5 +194,14 @@ public static string RequiredValidator_FeedbackMessage { return ResourceManager.GetString("RequiredValidator_FeedbackMessage", resourceCulture); } } + + /// + /// Looks up a localized string similar to The value must be a valid URL, e.g. https://example.com.. + /// + public static string UrlValidator_FeedbackMessage { + get { + return ResourceManager.GetString("UrlValidator_FeedbackMessage", resourceCulture); + } + } } } diff --git a/src/MADE.Data.Validation/Strings/Resources.resx b/src/MADE.Data.Validation/Strings/Resources.resx index c818422e..a64eb91a 100644 --- a/src/MADE.Data.Validation/Strings/Resources.resx +++ b/src/MADE.Data.Validation/Strings/Resources.resx @@ -123,15 +123,24 @@ The value must only contain letters. + + The value must be a valid base64 string. + The value must be between {0} and {1}. The value must be a valid email address, e.g. test@example.com. + + The value must be a valid GUID. + The value must be a valid IP address, e.g. 192.168.0.1. + + The value must be a valid MAC address, e.g. 00:11:22:33:44:55. + The length must be less than {0}. @@ -144,10 +153,16 @@ The value must be greater than {0}. + + The value is not valid. + The value does not match the valid mask. A value is required. + + The value must be a valid URL, e.g. https://example.com. + \ No newline at end of file diff --git a/src/MADE.Data.Validation/ValidatorCollection.cs b/src/MADE.Data.Validation/ValidatorCollection.cs index fca8d774..bbe57e67 100644 --- a/src/MADE.Data.Validation/ValidatorCollection.cs +++ b/src/MADE.Data.Validation/ValidatorCollection.cs @@ -11,7 +11,7 @@ namespace MADE.Data.Validation /// /// Defines a list of objects that can be accessed by index. /// - public class ValidatorCollection : List + public class ValidatorCollection : List, IValidatorCollection { /// Initializes a new instance of the class that is empty and has the default initial capacity. public ValidatorCollection() diff --git a/src/MADE.Data.Validation/Validators/Base64Validator.cs b/src/MADE.Data.Validation/Validators/Base64Validator.cs new file mode 100644 index 00000000..65c8a326 --- /dev/null +++ b/src/MADE.Data.Validation/Validators/Base64Validator.cs @@ -0,0 +1,57 @@ +// MADE Apps licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace MADE.Data.Validation.Validators +{ + using System.Text.RegularExpressions; + using MADE.Data.Validation.Extensions; + using MADE.Data.Validation.Strings; + + /// + /// Defines a data validator for ensuring a value is a valid base64 value. + /// + public class Base64Validator : RegexValidator + { + private string feedbackMessage; + + /// + /// Initializes a new instance of the class. + /// + public Base64Validator() + { + this.Key = nameof(Base64Validator); + this.Pattern = @"^[a-zA-Z0-9\+/]*={0,3}$"; + } + + /// + /// Gets or sets the feedback message to display when is true. + /// + public override string FeedbackMessage + { + get => this.feedbackMessage.IsNullOrWhiteSpace() + ? Resources.Base64Validator_FeedbackMessage + : this.feedbackMessage; + set => this.feedbackMessage = value; + } + + /// + /// Executes data validation on the provided . + /// + /// The value to be validated. + /// Thrown if a Regex time-out occurred. + public override void Validate(object value) + { + var stringValue = value?.ToString() ?? string.Empty; + if (stringValue.Length % 4 != 0) + { + this.IsInvalid = true; + } + else + { + base.Validate(value); + } + + this.IsDirty = true; + } + } +} \ No newline at end of file diff --git a/src/MADE.Data.Validation/Validators/BetweenValidator.cs b/src/MADE.Data.Validation/Validators/BetweenValidator.cs index e3f20ff1..940503e2 100644 --- a/src/MADE.Data.Validation/Validators/BetweenValidator.cs +++ b/src/MADE.Data.Validation/Validators/BetweenValidator.cs @@ -52,7 +52,9 @@ public BetweenValidator(IComparable min, IComparable max) /// public string FeedbackMessage { - get => this.feedbackMessage.IsNullOrWhiteSpace() ? string.Format(Resources.BetweenValidator_FeedbackMessage, this.Min, this.Max) : this.feedbackMessage; + get => this.feedbackMessage.IsNullOrWhiteSpace() + ? string.Format(Resources.BetweenValidator_FeedbackMessage, this.Min, this.Max) + : this.feedbackMessage; set => this.feedbackMessage = value; } @@ -66,6 +68,14 @@ public string FeedbackMessage /// public IComparable Max { get; set; } + /// + /// Gets or sets a value indicating whether the range is inclusive. + /// + /// + /// By default, the value is true. + /// + public bool Inclusive { get; set; } = true; + /// /// Executes data validation on the provided . /// @@ -76,7 +86,14 @@ public void Validate(object value) if (value is IComparable comparable) { - isInvalid = comparable.IsLessThan(this.Min) || comparable.IsGreaterThan(this.Max); + if (this.Inclusive) + { + isInvalid = comparable.IsLessThan(this.Min) || comparable.IsGreaterThan(this.Max); + } + else + { + isInvalid = comparable.IsLessThanOrEqualTo(this.Min) || comparable.IsGreaterThanOrEqualTo(this.Max); + } } this.IsInvalid = isInvalid; diff --git a/src/MADE.Data.Validation/Validators/GuidValidator.cs b/src/MADE.Data.Validation/Validators/GuidValidator.cs new file mode 100644 index 00000000..321a100f --- /dev/null +++ b/src/MADE.Data.Validation/Validators/GuidValidator.cs @@ -0,0 +1,65 @@ +// MADE Apps licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace MADE.Data.Validation.Validators +{ + using System; + using MADE.Data.Validation.Extensions; + using MADE.Data.Validation.Strings; + + /// + /// Defines a data validator for ensuring a value is a . + /// + public class GuidValidator : IValidator + { + private string feedbackMessage; + + /// + /// Gets or sets the key associated with the validator. + /// + public string Key { get; set; } = nameof(GuidValidator); + + /// + /// Gets or sets a value indicating whether the data provided is in an invalid state. + /// + public bool IsInvalid { get; set; } + + /// + /// Gets or sets a value indicating whether the data is dirty. + /// + public bool IsDirty { get; set; } + + /// + /// Gets or sets the feedback message to display when is true. + /// + public string FeedbackMessage + { + get => this.feedbackMessage.IsNullOrWhiteSpace() + ? Resources.GuidValidator_FeedbackMessage + : this.feedbackMessage; + set => this.feedbackMessage = value; + } + + /// + /// Executes data validation on the provided . + /// + /// The value to be validated. + public void Validate(object value) + { + bool isInvalid; + + if (value is Guid) + { + isInvalid = false; + } + else + { + var stringValue = value?.ToString() ?? string.Empty; + isInvalid = !Guid.TryParse(stringValue, out _); + } + + this.IsInvalid = isInvalid; + this.IsDirty = true; + } + } +} \ No newline at end of file diff --git a/src/MADE.Data.Validation/Validators/LatitudeValidator.cs b/src/MADE.Data.Validation/Validators/LatitudeValidator.cs new file mode 100644 index 00000000..f2c4e0c6 --- /dev/null +++ b/src/MADE.Data.Validation/Validators/LatitudeValidator.cs @@ -0,0 +1,64 @@ +// MADE Apps licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace MADE.Data.Validation.Validators +{ + using System; + using MADE.Data.Validation.Extensions; + using MADE.Data.Validation.Strings; + + /// + /// Defines a data validator for ensuring a value is within the valid range for a latitude value. + /// + public class LatitudeValidator : IValidator + { + /// + /// The minimum valid latitude value. + /// + public const double Min = -90; + + /// + /// The maximum valid latitude value. + /// + public const double Max = 90; + + private string feedbackMessage; + + /// + /// Gets or sets the key associated with the validator. + /// + public virtual string Key { get; set; } = nameof(LatitudeValidator); + + /// + /// Gets or sets a value indicating whether the data provided is in an invalid state. + /// + public bool IsInvalid { get; set; } + + /// + /// Gets or sets a value indicating whether the data is dirty. + /// + public bool IsDirty { get; set; } + + /// + /// Gets or sets the feedback message to display when is true. + /// + public virtual string FeedbackMessage + { + get => this.feedbackMessage.IsNullOrWhiteSpace() + ? string.Format(Resources.BetweenValidator_FeedbackMessage, Min, Max) + : this.feedbackMessage; + set => this.feedbackMessage = value; + } + + /// + /// Executes data validation on the provided . + /// + /// The value to be validated. + public void Validate(object value) + { + bool parsed = double.TryParse(value?.ToString() ?? string.Empty, out double latitude); + this.IsInvalid = !parsed || latitude is < Min or > Max; + this.IsDirty = true; + } + } +} \ No newline at end of file diff --git a/src/MADE.Data.Validation/Validators/LongitudeValidator.cs b/src/MADE.Data.Validation/Validators/LongitudeValidator.cs new file mode 100644 index 00000000..321658cf --- /dev/null +++ b/src/MADE.Data.Validation/Validators/LongitudeValidator.cs @@ -0,0 +1,64 @@ +// MADE Apps licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace MADE.Data.Validation.Validators +{ + using System; + using MADE.Data.Validation.Extensions; + using MADE.Data.Validation.Strings; + + /// + /// Defines a data validator for ensuring a value is within the valid range for a longitude value. + /// + public class LongitudeValidator : IValidator + { + /// + /// The minimum valid longitude value. + /// + public const double Min = -180; + + /// + /// The maximum valid longitude value. + /// + public const double Max = 180; + + private string feedbackMessage; + + /// + /// Gets or sets the key associated with the validator. + /// + public virtual string Key { get; set; } = nameof(LongitudeValidator); + + /// + /// Gets or sets a value indicating whether the data provided is in an invalid state. + /// + public bool IsInvalid { get; set; } + + /// + /// Gets or sets a value indicating whether the data is dirty. + /// + public bool IsDirty { get; set; } + + /// + /// Gets or sets the feedback message to display when is true. + /// + public virtual string FeedbackMessage + { + get => this.feedbackMessage.IsNullOrWhiteSpace() + ? string.Format(Resources.BetweenValidator_FeedbackMessage, Min, Max) + : this.feedbackMessage; + set => this.feedbackMessage = value; + } + + /// + /// Executes data validation on the provided . + /// + /// The value to be validated. + public void Validate(object value) + { + bool parsed = double.TryParse(value?.ToString() ?? string.Empty, out double longitude); + this.IsInvalid = !parsed || longitude is < Min or > Max; + this.IsDirty = true; + } + } +} \ No newline at end of file diff --git a/src/MADE.Data.Validation/Validators/MacAddressValidator.cs b/src/MADE.Data.Validation/Validators/MacAddressValidator.cs new file mode 100644 index 00000000..12e99e10 --- /dev/null +++ b/src/MADE.Data.Validation/Validators/MacAddressValidator.cs @@ -0,0 +1,68 @@ +// MADE Apps licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace MADE.Data.Validation.Validators +{ + using System; + using System.Net.NetworkInformation; + using MADE.Data.Validation.Extensions; + using MADE.Data.Validation.Strings; + + /// + /// Defines a data validator for ensuring a value is a valid MAC address. + /// + public class MacAddressValidator : IValidator + { + private string feedbackMessage; + + /// + /// Gets or sets the key associated with the validator. + /// + public string Key { get; set; } = nameof(MacAddressValidator); + + /// + /// Gets or sets a value indicating whether the data provided is in an invalid state. + /// + public bool IsInvalid { get; set; } + + /// + /// Gets or sets a value indicating whether the data is dirty. + /// + public bool IsDirty { get; set; } + + /// + /// Gets or sets the feedback message to display when is true. + /// + public string FeedbackMessage + { + get => this.feedbackMessage.IsNullOrWhiteSpace() + ? Resources.MacAddressValidator_FeedbackMessage + : this.feedbackMessage; + set => this.feedbackMessage = value; + } + + /// + /// Executes data validation on the provided . + /// + /// The value to be validated. + /// The array is multidimensional and contains more than elements. + public void Validate(object value) + { + bool isInvalid; + var stringValue = value?.ToString() ?? string.Empty; + + try + { + PhysicalAddress newAddress = PhysicalAddress.Parse(stringValue); + isInvalid = PhysicalAddress.None.Equals(newAddress); + } + catch (FormatException) + { + isInvalid = true; + } + + this.IsInvalid = isInvalid; + this.IsDirty = true; + } + } +} \ No newline at end of file diff --git a/src/MADE.Data.Validation/Validators/PredicateValidator{T}.cs b/src/MADE.Data.Validation/Validators/PredicateValidator{T}.cs new file mode 100644 index 00000000..57a57384 --- /dev/null +++ b/src/MADE.Data.Validation/Validators/PredicateValidator{T}.cs @@ -0,0 +1,78 @@ +// MADE Apps licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace MADE.Data.Validation.Validators +{ + using System; + using System.Text.RegularExpressions; + using MADE.Data.Validation.Extensions; + using MADE.Data.Validation.Strings; + + /// + /// Defines a generic data validator that performs custom validation logic based on the value. + /// + /// The type of value being validated. + public class PredicateValidator : IValidator + { + private string feedbackMessage; + + /// + /// Initializes a new instance of the class. + /// + public PredicateValidator() + { + } + + /// + /// Initializes a new instance of the class with the custom validation logic. + /// + /// The logic for performing validation on the value. + public PredicateValidator(Func predicate) + { + this.Predicate = predicate; + } + + /// + /// Gets or sets the key associated with the validator. + /// + public string Key { get; set; } = nameof(PredicateValidator); + + /// + /// Gets or sets a value indicating whether the data provided is in an invalid state. + /// + public bool IsInvalid { get; set; } + + /// + /// Gets or sets a value indicating whether the data is dirty. + /// + public bool IsDirty { get; set; } + + /// + /// Gets or sets the feedback message to display when is true. + /// + public virtual string FeedbackMessage + { + get => this.feedbackMessage.IsNullOrWhiteSpace() + ? Resources.PredicateValidator_FeedbackMessage + : this.feedbackMessage; + set => this.feedbackMessage = value; + } + + /// + /// Gets or sets the logic for performing validation on the value. + /// + public Func Predicate { get; set; } + + /// + /// Executes data validation on the provided . + /// + /// The value to be validated. + /// Thrown if a Regex time-out occurred. + public void Validate(object value) + { + var typedValue = (T)value; + this.IsInvalid = !this.Predicate(typedValue); + this.IsDirty = true; + } + } +} \ No newline at end of file diff --git a/src/MADE.Data.Validation/Validators/RegexValidator.cs b/src/MADE.Data.Validation/Validators/RegexValidator.cs index fd6126fe..31164d8f 100644 --- a/src/MADE.Data.Validation/Validators/RegexValidator.cs +++ b/src/MADE.Data.Validation/Validators/RegexValidator.cs @@ -48,7 +48,7 @@ public virtual string FeedbackMessage /// /// The value to be validated. /// Thrown if a Regex time-out occurred. - public void Validate(object value) + public virtual void Validate(object value) { string str = value?.ToString() ?? string.Empty; this.IsInvalid = !Regex.IsMatch(str, this.Pattern); diff --git a/src/MADE.Data.Validation/Validators/WellFormedUrlValidator.cs b/src/MADE.Data.Validation/Validators/WellFormedUrlValidator.cs new file mode 100644 index 00000000..da2463e5 --- /dev/null +++ b/src/MADE.Data.Validation/Validators/WellFormedUrlValidator.cs @@ -0,0 +1,65 @@ +// MADE Apps licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace MADE.Data.Validation.Validators +{ + using System; + using MADE.Data.Validation.Extensions; + using MADE.Data.Validation.Strings; + + /// + /// Defines a data validator for ensuring a value is a valid well formed URL, e.g. https://www.example.com. + /// + public class WellFormedUrlValidator : IValidator + { + private string feedbackMessage; + + /// + /// Gets or sets the key associated with the validator. + /// + public string Key { get; set; } = nameof(WellFormedUrlValidator); + + /// + /// Gets or sets a value indicating whether the data provided is in an invalid state. + /// + public bool IsInvalid { get; set; } + + /// + /// Gets or sets a value indicating whether the data is dirty. + /// + public bool IsDirty { get; set; } + + /// + /// Gets or sets the feedback message to display when is true. + /// + public string FeedbackMessage + { + get => this.feedbackMessage.IsNullOrWhiteSpace() + ? Resources.UrlValidator_FeedbackMessage + : this.feedbackMessage; + set => this.feedbackMessage = value; + } + + /// + /// Executes data validation on the provided . + /// + /// The value to be validated. + public void Validate(object value) + { + bool isInvalid; + + if (value is Uri uri) + { + isInvalid = uri.IsWellFormedOriginalString() == false; + } + else + { + var stringValue = value?.ToString() ?? string.Empty; + isInvalid = !Uri.IsWellFormedUriString(stringValue, UriKind.Absolute); + } + + this.IsInvalid = isInvalid; + this.IsDirty = true; + } + } +} \ No newline at end of file diff --git a/src/MADE.Foundation/MADE.Foundation.csproj b/src/MADE.Foundation/MADE.Foundation.csproj index 61285596..ca94de7b 100644 --- a/src/MADE.Foundation/MADE.Foundation.csproj +++ b/src/MADE.Foundation/MADE.Foundation.csproj @@ -1,7 +1,7 @@ - uap10.0.17763;MonoAndroid10.0;xamarinios10;netstandard2.0;xamarinmac20 + netstandard2.0 true MADE.NET Foundation diff --git a/src/MADE.Media.Image/Behaviors/LoadStorageFileThumbnailImageBehavior.cs b/src/MADE.Media.Image/Behaviors/LoadStorageFileThumbnailImageBehavior.cs deleted file mode 100644 index 5d699f6a..00000000 --- a/src/MADE.Media.Image/Behaviors/LoadStorageFileThumbnailImageBehavior.cs +++ /dev/null @@ -1,67 +0,0 @@ -// MADE Apps licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace MADE.Media.Image.Behaviors -{ - using System; - using System.Threading.Tasks; - using Microsoft.Xaml.Interactivity; - using Windows.Storage; - using Windows.Storage.FileProperties; - using Windows.UI.Xaml; - using Windows.UI.Xaml.Controls; - using Windows.UI.Xaml.Media.Imaging; - - /// - /// Defines a behavior for loading a storage file's thumbnail image into a control. - /// - public class LoadStorageFileThumbnailImageBehavior : Behavior - { - /// - /// Identifies the dependency property. - /// - public static readonly DependencyProperty FileProperty = DependencyProperty.Register( - nameof(File), - typeof(StorageFile), - typeof(LoadStorageFileThumbnailImageBehavior), - new PropertyMetadata( - null, - async (d, e) => - await ((LoadStorageFileThumbnailImageBehavior)d).UpdateImageSourceAsync((StorageFile)e.NewValue))); - - /// - /// Gets or sets the storage file to retrieve a thumbnail for. - /// - public StorageFile File - { - get => (StorageFile)this.GetValue(FileProperty); - set => this.SetValue(FileProperty, value); - } - - private async Task UpdateImageSourceAsync(IStorageItemProperties file) - { - if (file == null) - { - return; - } - - StorageItemThumbnail thumbnail = await file.GetThumbnailAsync( - ThumbnailMode.SingleItem, - 256, - ThumbnailOptions.ResizeThumbnail); - - if (thumbnail == null) - { - return; - } - - if (this.AssociatedObject != null) - { - var bitmapImage = new BitmapImage(); - bitmapImage.SetSource(thumbnail.CloneStream()); - - this.AssociatedObject.Source = bitmapImage; - } - } - } -} diff --git a/src/MADE.Media.Image/MADE.Media.Image.csproj b/src/MADE.Media.Image/MADE.Media.Image.csproj deleted file mode 100644 index 898b3184..00000000 --- a/src/MADE.Media.Image/MADE.Media.Image.csproj +++ /dev/null @@ -1,18 +0,0 @@ - - - - uap10.0.17763 - true - MADE.NET Images - - This package includes: - - LoadStorageFileThumbnailImageBehavior for providing the ability to show a thumbnail for a storage file on an Image. - - MADE Media Images Thumbnail StorageFile - - - - - - - \ No newline at end of file diff --git a/src/MADE.Runtime/Extensions/ReflectionExtensions.cs b/src/MADE.Runtime/Extensions/ReflectionExtensions.cs index 7c65ba8e..e320f1fe 100644 --- a/src/MADE.Runtime/Extensions/ReflectionExtensions.cs +++ b/src/MADE.Runtime/Extensions/ReflectionExtensions.cs @@ -1,6 +1,7 @@ namespace MADE.Runtime.Extensions { using System; + using System.Collections.Generic; using System.Reflection; /// @@ -23,5 +24,19 @@ public static T GetPropertyValue(this object obj, string property) PropertyInfo prop = type.GetProperty(property); return prop?.GetValue(obj) as T; } + + /// + /// Gets all the property names declared for the specified object. + /// + /// The object to retrieve property names from. + /// A collection of object property names as a string. + public static IEnumerable GetPropertyNames(this object obj) + { + Type type = obj.GetType(); + foreach (PropertyInfo property in type.GetTypeInfo().DeclaredProperties) + { + yield return property.Name; + } + } } } \ No newline at end of file diff --git a/tests/MADE.Collections.Tests/MADE.Collections.Tests.csproj b/tests/MADE.Collections.Tests/MADE.Collections.Tests.csproj index 2d03ee7d..8141ae1f 100644 --- a/tests/MADE.Collections.Tests/MADE.Collections.Tests.csproj +++ b/tests/MADE.Collections.Tests/MADE.Collections.Tests.csproj @@ -1,21 +1,20 @@ - net5.0 - + net6.0 false - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + - + diff --git a/tests/MADE.Collections.Tests/Tests/CollectionExtensionsTests.cs b/tests/MADE.Collections.Tests/Tests/CollectionExtensionsTests.cs index f434484f..2bfb514b 100644 --- a/tests/MADE.Collections.Tests/Tests/CollectionExtensionsTests.cs +++ b/tests/MADE.Collections.Tests/Tests/CollectionExtensionsTests.cs @@ -75,7 +75,7 @@ public void ShouldThrowArgumentNullExceptionIfNullCollection() public void ShouldThrowArgumentNullExceptionIfNullItem() { // Arrange - var list = new List {"Hello"}; + var list = new List { "Hello" }; string item = null; // Act & Assert @@ -89,7 +89,7 @@ public void ShouldReturnTrueIfItemUpdated() TestObject objectToAdd = TestObjectFaker.Create().Generate(); TestObject objectToUpdateWith = TestObjectFaker.Create().Generate(); - var list = new List {objectToAdd}; + var list = new List { objectToAdd }; // Act bool updated = list.Update(objectToUpdateWith, (s, i) => s.Name == objectToAdd.Name); @@ -105,7 +105,7 @@ public void ShouldReturnFalseIfItemToUpdateDoesNotExist() TestObject objectToAdd = TestObjectFaker.Create().Generate(); TestObject objectToUpdateWith = TestObjectFaker.Create().Generate(); - var list = new List {objectToAdd}; + var list = new List { objectToAdd }; // Act bool updated = list.Update(objectToUpdateWith, (s, i) => s.Name == objectToUpdateWith.Name); @@ -131,7 +131,7 @@ public void ShouldThrowArgumentNullExceptionIfNullCollection() public void ShouldThrowArgumentNullExceptionIfNullSource() { // Arrange - var list = new List {"Hello"}; + var list = new List { "Hello" }; // Act & Assert Assert.Throws(() => list.MakeEqualTo(null)); @@ -141,8 +141,8 @@ public void ShouldThrowArgumentNullExceptionIfNullSource() public void ShouldUpdateCollectionToBeEqualOther() { // Arrange - var list = new List {"Hello"}; - var update = new List {"New", "List"}; + var list = new List { "Hello" }; + var update = new List { "New", "List" }; // Act list.MakeEqualTo(update); @@ -224,5 +224,185 @@ public void ShouldReturnFalseForInvalidCases(Collection expected, Collectio new object[] {new ObservableCollection {1, 2, 3}, new ObservableCollection {1, 2, 3, 4}}, }; } + + public class WhenValidatingIfCollectionIsNullOrEmpty + { + [TestCaseSource(nameof(ValidEnumerableCases))] + public void ShouldReturnTrueIfEnumerableIsNullOrEmpty(IEnumerable collection) + { + // Act + bool isEmpty = collection.IsNullOrEmpty(); + + // Assert + isEmpty.ShouldBeTrue(); + } + + [TestCaseSource(nameof(ValidDictionaryCases))] + public void ShouldReturnTrueIfDictionaryIsNullOrEmpty(Dictionary collection) + { + // Act + bool isEmpty = collection.IsNullOrEmpty(); + + // Assert + isEmpty.ShouldBeTrue(); + } + + [TestCaseSource(nameof(InvalidEnumerableCases))] + public void ShouldReturnFalseIfEnumerableIsNotNullOrEmpty(IEnumerable collection) + { + // Act + bool isEmpty = collection.IsNullOrEmpty(); + + // Assert + isEmpty.ShouldBeFalse(); + } + + [TestCaseSource(nameof(InvalidDictionaryCases))] + public void ShouldReturnFalseIfDictionaryIsNotNullOrEmpty(Dictionary collection) + { + // Act + bool isEmpty = collection.IsNullOrEmpty(); + + // Assert + isEmpty.ShouldBeFalse(); + } + + private static object[] ValidEnumerableCases = + { + new object[] {null}, new object[] {new ObservableCollection()}, + }; + + private static object[] ValidDictionaryCases = + { + new object[] {null}, new object[] {new Dictionary()}, + }; + + private static object[] InvalidEnumerableCases = + { + new object[] {new ObservableCollection {1, 2, 3}}, + }; + + private static object[] InvalidDictionaryCases = + { + new object[] {new Dictionary {{1, "A"}, {2, "B"}, {3, "C"}}}, + }; + } + + public class WhenSortingObservableCollections + { + [Test] + public void ShouldSortBySimpleType() + { + // Arrange + var collection = new ObservableCollection { 3, 2, 1 }; + + // Act + collection.Sort(x => x); + + // Assert + collection.ShouldBe(new[] { 1, 2, 3 }); + } + + [Test] + public void ShouldSortByComplexType() + { + // Arrange + var collection = new ObservableCollection + { + new() {Id = 0, Name = "James Croft"}, + new() {Id = 1, Name = "Guy Wilmer"}, + new() {Id = 2, Name = "Ben Hartley"}, + new() {Id = 3, Name = "Adam Llewellyn"}, + }; + + // Act + collection.Sort(x => x.Name); + + // Assert + collection.ShouldBe(new ComplexObject[] + { + new() {Id = 3, Name = "Adam Llewellyn"}, new() {Id = 2, Name = "Ben Hartley"}, + new() {Id = 1, Name = "Guy Wilmer"}, new() {Id = 0, Name = "James Croft"}, + }); + } + + [Test] + public void ShouldSortDescendingBySimpleType() + { + // Arrange + var collection = new ObservableCollection { 2, 1, 3 }; + + // Act + collection.SortDescending(x => x); + + // Assert + collection.ShouldBe(new[] { 3, 2, 1 }); + } + + [Test] + public void ShouldSortDescendingByComplexType() + { + // Arrange + var collection = new ObservableCollection + { + new() {Id = 0, Name = "Ben Hartley"}, + new() {Id = 1, Name = "James Croft"}, + new() {Id = 2, Name = "Adam Llewellyn"}, + new() {Id = 3, Name = "Guy Wilmer"}, + }; + + // Act + collection.SortDescending(x => x.Name); + + // Assert + collection.ShouldBe(new ComplexObject[] + { + new() {Id = 1, Name = "James Croft"}, new() {Id = 3, Name = "Guy Wilmer"}, + new() {Id = 0, Name = "Ben Hartley"}, new() {Id = 2, Name = "Adam Llewellyn"}, + }); + } + + private class ComplexObject : IEquatable + { + public int Id { get; set; } + + public string Name { get; set; } + + public bool Equals(ComplexObject other) + { + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + + return this.Id == other.Id && this.Name == other.Name; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + return obj.GetType() == this.GetType() && this.Equals((ComplexObject)obj); + } + + public override int GetHashCode() + { + return HashCode.Combine(this.Id, this.Name); + } + } + } } } \ No newline at end of file diff --git a/tests/MADE.Data.Converters.Tests/MADE.Data.Converters.Tests.csproj b/tests/MADE.Data.Converters.Tests/MADE.Data.Converters.Tests.csproj index aac84e4c..17a848c5 100644 --- a/tests/MADE.Data.Converters.Tests/MADE.Data.Converters.Tests.csproj +++ b/tests/MADE.Data.Converters.Tests/MADE.Data.Converters.Tests.csproj @@ -1,21 +1,20 @@ - net5.0 - + net6.0 false - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + - + diff --git a/tests/MADE.Data.Converters.Tests/Tests/BooleanExtensionsTests.cs b/tests/MADE.Data.Converters.Tests/Tests/BooleanExtensionsTests.cs new file mode 100644 index 00000000..c2d26776 --- /dev/null +++ b/tests/MADE.Data.Converters.Tests/Tests/BooleanExtensionsTests.cs @@ -0,0 +1,88 @@ +namespace MADE.Data.Converters.Tests.Tests +{ + using System.Diagnostics.CodeAnalysis; + using MADE.Data.Converters.Extensions; + using NUnit.Framework; + using Shouldly; + + [ExcludeFromCodeCoverage] + [TestFixture] + public class BooleanExtensionsTests + { + public class WhenConvertingBooleanToFormattedString + { + [Test] + public void ShouldReturnTrueValueIfTrue() + { + // Arrange + const bool boolean = true; + const string expected = "Yes"; + + // Act + string formatted = boolean.ToFormattedString(expected, "No"); + + // Assert + formatted.ShouldBe(expected); + } + + [Test] + public void ShouldReturnFalseValueIfFalse() + { + // Arrange + const bool boolean = false; + const string expected = "No"; + + // Act + string formatted = boolean.ToFormattedString("Yes", expected); + + // Assert + formatted.ShouldBe(expected); + } + } + + public class WhenConvertingNullableBooleanToFormattedString + { + [Test] + public void ShouldReturnTrueValueIfTrue() + { + // Arrange + bool? boolean = true; + const string expected = "Yes"; + + // Act + string formatted = boolean.ToFormattedString(expected, "No", "N/A"); + + // Assert + formatted.ShouldBe(expected); + } + + [Test] + public void ShouldReturnFalseValueIfFalse() + { + // Arrange + bool? boolean = false; + const string expected = "No"; + + // Act + string formatted = boolean.ToFormattedString("Yes", expected, "N/A"); + + // Assert + formatted.ShouldBe(expected); + } + + [Test] + public void ShouldReturnNullValueIfNull() + { + // Arrange + bool? boolean = null; + const string expected = "N/A"; + + // Act + string formatted = boolean.ToFormattedString("Yes", "No", expected); + + // Assert + formatted.ShouldBe(expected); + } + } + } +} \ No newline at end of file diff --git a/tests/MADE.Data.Converters.Tests/Tests/BooleanToStringValueConverterTests.cs b/tests/MADE.Data.Converters.Tests/Tests/BooleanToStringValueConverterTests.cs new file mode 100644 index 00000000..4d298df8 --- /dev/null +++ b/tests/MADE.Data.Converters.Tests/Tests/BooleanToStringValueConverterTests.cs @@ -0,0 +1,94 @@ +namespace MADE.Data.Converters.Tests.Tests +{ + using System.Diagnostics.CodeAnalysis; + using MADE.Data.Converters.Exceptions; + using NUnit.Framework; + + using Shouldly; + + [ExcludeFromCodeCoverage] + [TestFixture] + public class BooleanToStringValueConverterTests + { + public class WhenConverting + { + [Test] + public void ShouldConvertToTrueValueWhenTrue() + { + // Arrange + const bool boolean = true; + const string expected = "Yes"; + + var converter = new BooleanToStringValueConverter {TrueValue = expected, FalseValue = "No"}; + + // Act + string converted = converter.Convert(boolean); + + // Assert + converted.ShouldBe(expected); + } + + [Test] + public void ShouldConvertToFalseValueWhenFalse() + { + const bool boolean = false; + const string expected = "No"; + + var converter = new BooleanToStringValueConverter {TrueValue = "Yes", FalseValue = expected}; + + // Act + string converted = converter.Convert(boolean); + + // Assert + converted.ShouldBe(expected); + } + } + + public class WhenConvertingBack + { + [Test] + public void ShouldConvertToTrueWhenTrueValue() + { + // Arrange + const string booleanString = "Yes"; + const bool expected = true; + + var converter = new BooleanToStringValueConverter {TrueValue = booleanString, FalseValue = "No"}; + + // Act + bool converted = converter.ConvertBack(booleanString); + + // Assert + converted.ShouldBe(expected); + } + + [Test] + public void ShouldConvertToFalseWhenFalseValue() + { + // Arrange + const string booleanString = "No"; + const bool expected = false; + + var converter = new BooleanToStringValueConverter {TrueValue = "Yes", FalseValue = booleanString}; + + // Act + bool converted = converter.ConvertBack(booleanString); + + // Assert + converted.ShouldBe(expected); + } + + [Test] + public void ShouldThrowInvalidDataConversionExceptionIfNotTrueOrFalseValue() + { + // Arrange + const string booleanString = "Not valid"; + + var converter = new BooleanToStringValueConverter {TrueValue = "Yes", FalseValue = "No"}; + + // Act & Assert + Should.Throw(() => converter.ConvertBack(booleanString)); + } + } + } +} \ No newline at end of file diff --git a/tests/MADE.Data.Converters.Tests/Tests/CollectionExtensionsTests.cs b/tests/MADE.Data.Converters.Tests/Tests/CollectionExtensionsTests.cs new file mode 100644 index 00000000..446c522d --- /dev/null +++ b/tests/MADE.Data.Converters.Tests/Tests/CollectionExtensionsTests.cs @@ -0,0 +1,41 @@ +namespace MADE.Data.Converters.Tests.Tests +{ + using System.Diagnostics.CodeAnalysis; + using MADE.Data.Converters.Extensions; + using NUnit.Framework; + using Shouldly; + + [ExcludeFromCodeCoverage] + [TestFixture] + public class CollectionExtensionsTests + { + public class WhenConvertingToDelimitedString + { + [Test] + public void ShouldReturnItemsWithCommaDelimiterByDefault() + { + // Arrange + var items = new[] { "item1", "item2", "item3" }; + + // Act + var result = items.ToDelimitedString(); + + // Assert + result.ShouldBe("item1,item2,item3"); + } + + [Test] + public void ShouldReturnItemsWithCustomDelimiter() + { + // Arrange + var items = new[] { "item1", "item2", "item3" }; + + // Act + var result = items.ToDelimitedString(delimiter: " | "); + + // Assert + result.ShouldBe("item1 | item2 | item3"); + } + } + } +} \ No newline at end of file diff --git a/tests/MADE.Data.Serialization.Tests/MADE.Data.Serialization.Tests.csproj b/tests/MADE.Data.Serialization.Tests/MADE.Data.Serialization.Tests.csproj new file mode 100644 index 00000000..c2b05b86 --- /dev/null +++ b/tests/MADE.Data.Serialization.Tests/MADE.Data.Serialization.Tests.csproj @@ -0,0 +1,25 @@ + + + + net6.0 + false + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + diff --git a/tests/MADE.Data.Serialization.Tests/Tests/JsonTypeMigrationSerializationBinderTests.cs b/tests/MADE.Data.Serialization.Tests/Tests/JsonTypeMigrationSerializationBinderTests.cs new file mode 100644 index 00000000..7a5ae889 --- /dev/null +++ b/tests/MADE.Data.Serialization.Tests/Tests/JsonTypeMigrationSerializationBinderTests.cs @@ -0,0 +1,85 @@ +namespace MADE.Data.Serialization.Tests.Tests +{ + using System.Diagnostics.CodeAnalysis; + using System.Threading.Tasks; + using MADE.Data.Serialization.Json; + using MADE.Data.Serialization.Json.Binders; + using Newtonsoft.Json; + using NUnit.Framework; + using Shouldly; + + [ExcludeFromCodeCoverage] + [TestFixture] + public class JsonTypeMigrationSerializationBinderTests + { + public class WhenMigratingFromOneTypeToAnother + { + [Test] + public async Task ShouldMigrateFromTypeToType() + { + // Arrange + var binder = new JsonTypeMigrationSerializationBinder(); + await binder.AddTypeMigrationAsync(new JsonTypeMigration(typeof(OldType), typeof(NewType))); + + var oldType = new OldType(); + var serialized = JsonConvert.SerializeObject( + oldType, + new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.All}); + + // Act + var deserialized = JsonConvert.DeserializeObject( + serialized, + new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.All, SerializationBinder = binder}); + + // Assert + deserialized.ShouldBeOfType(typeof(NewType)); + + var newType = (NewType)deserialized; + newType.Name.ShouldBe(oldType.Name); + newType.Number.ShouldBe((double)oldType.Number); + } + + [Test] + public async Task ShouldMigrateFromAssemblyAndTypeNameToType() + { + // Arrange + var binder = new JsonTypeMigrationSerializationBinder(); + await binder.AddTypeMigrationAsync(new JsonTypeMigration( + "MADE.Data.Serialization.Tests", + "MADE.Data.Serialization.Tests.Tests.JsonTypeMigrationSerializationBinderTests+OldType", + typeof(NewType))); + + var oldType = new OldType(); + var serialized = JsonConvert.SerializeObject( + oldType, + new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.All}); + + // Act + var deserialized = JsonConvert.DeserializeObject( + serialized, + new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.All, SerializationBinder = binder}); + + // Assert + deserialized.ShouldBeOfType(typeof(NewType)); + + var newType = (NewType)deserialized; + newType.Name.ShouldBe(oldType.Name); + newType.Number.ShouldBe((double)oldType.Number); + } + } + + private class OldType + { + public string Name { get; set; } + + public int Number { get; set; } + } + + private class NewType + { + public string Name { get; set; } + + public double Number { get; set; } + } + } +} \ No newline at end of file diff --git a/tests/MADE.Data.Validation.FluentValidation.Tests/MADE.Data.Validation.FluentValidation.Tests.csproj b/tests/MADE.Data.Validation.FluentValidation.Tests/MADE.Data.Validation.FluentValidation.Tests.csproj new file mode 100644 index 00000000..b4ec8877 --- /dev/null +++ b/tests/MADE.Data.Validation.FluentValidation.Tests/MADE.Data.Validation.FluentValidation.Tests.csproj @@ -0,0 +1,27 @@ + + + + net6.0 + false + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/MADE.Data.Validation.FluentValidation.Tests/Tests/FluentValidatorCollectionTests.cs b/tests/MADE.Data.Validation.FluentValidation.Tests/Tests/FluentValidatorCollectionTests.cs new file mode 100644 index 00000000..44689a2f --- /dev/null +++ b/tests/MADE.Data.Validation.FluentValidation.Tests/Tests/FluentValidatorCollectionTests.cs @@ -0,0 +1,230 @@ +namespace MADE.Data.Validation.FluentValidation.Tests.Tests +{ + using System; + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using System.Linq; + using System.Threading; + using System.Threading.Tasks; + using global::FluentValidation; + using global::FluentValidation.Results; + using MADE.Testing; + using NUnit.Framework; + using Shouldly; + + [ExcludeFromCodeCoverage] + [TestFixture] + public class FluentValidatorCollectionTests + { + public class WhenInitializing + { + [Test] + public void ShouldBeEmptyIfDefaultConstructor() + { + // Act + var collection = new FluentValidatorCollection(); + + // Assert + collection.Count.ShouldBe(0); + } + + [Test] + public void ShouldContainItemsIfInitializedAsEnumerable() + { + // Arrange + IEnumerable> validators = new List> + { + new PersonValidator() + }; + + // Act + var collection = new FluentValidatorCollection(validators); + + // Assert + collection.Count.ShouldBe(validators.Count()); + collection.ToList().ShouldBeEquivalentTo(validators); + } + } + + public class WhenAddingItems + { + [Test] + public void ShouldAddRangeOfItems() + { + // Arrange + IEnumerable> validators = new List> + { + new PersonValidator() + }; + + var collection = new FluentValidatorCollection(); + + // Act + collection.AddRange(validators); + + // Assert + foreach (AbstractValidator item in validators) + { + collection.ShouldContain(item); + } + } + + [Test] + public void ShouldAddSingleItem() + { + // Arrange + var objectToAdd = new PersonValidator(); + + // Act + var collection = new FluentValidatorCollection { objectToAdd }; + + // Assert + collection.ShouldContain(objectToAdd); + } + } + + public class WhenValidating + { + [Test] + public void ShouldBeDirtyOnceValidated() + { + // Arrange + var value = new Person(); + + var collection = new FluentValidatorCollection { new PersonValidator() }; + + // Act + collection.Validate(value); + + // Assert + collection.IsDirty.ShouldBe(true); + } + + [Test] + public void ShouldBeValidIfValidValue() + { + // Arrange + var value = new Person { Name = "Joe Bloggs", DateOfBirth = new DateTime(1992, 01, 01) }; + + var collection = new FluentValidatorCollection { new PersonValidator() }; + + // Act + collection.Validate(value); + + // Assert + collection.IsInvalid.ShouldBe(false); + } + + [Test] + public void ShouldBeInvalidIfInvalidValue() + { + // Arrange + var value = new Person(); + + var collection = new FluentValidatorCollection { new PersonValidator() }; + + // Act + collection.Validate(value); + + // Assert + collection.IsInvalid.ShouldBe(true); + } + + [Test] + public void ShouldHaveFeedbackMessagesIfInvalidValue() + { + // Arrange + var value = new Person + { + Name = "Joe Bloggs", + DateOfBirth = DateTime.UtcNow.AddDays(1) // Invalid birth date + }; + + var collection = new FluentValidatorCollection { new PersonValidator() }; + + // Act + collection.Validate(value); + + // Assert + collection.FeedbackMessages.ShouldNotBeEmpty(); + collection.FeedbackMessages.Count().ShouldBe(1); + collection.FeedbackMessages.FirstOrDefault().ShouldBe(PersonValidator.DateOfBirthValidationMessage); + } + + [Test] + public void ShouldValidateComplexObjectWithMultipleValidators() + { + // Arrange + var value = new Staff + { + Name = "Joe Bloggs", + JobTitle = null, // Invalid job title + Department = "Build", + DateOfBirth = DateTime.UtcNow.AddDays(1) // Invalid birth date + }; + + var collection = new FluentValidatorCollection { new PersonValidator(), new StaffValidator() }; + + // Act + collection.Validate(value); + + // Assert + collection.FeedbackMessages.ShouldNotBeEmpty(); + collection.FeedbackMessages.Count().ShouldBe(2); + collection.FeedbackMessages.FirstOrDefault(x => x.Equals(PersonValidator.DateOfBirthValidationMessage, + StringComparison.InvariantCultureIgnoreCase)).ShouldNotBeNull(); + collection.FeedbackMessages.FirstOrDefault(x => x.Equals(StaffValidator.JobTitleValidationMessage, + StringComparison.InvariantCultureIgnoreCase)).ShouldNotBeNull(); + } + } + } + + public class Person + { + public string Name { get; set; } + + public DateTime? DateOfBirth { get; set; } + } + + public class Staff : Person + { + public string JobTitle { get; set; } + + public string Department { get; set; } + } + + public class PersonValidator : AbstractValidator + { + public const string DateOfBirthValidationMessage = "Please specify a valid date of birth"; + + public PersonValidator() + { + this.RuleFor(x => x.Name).NotEmpty(); + this.RuleFor(x => x.DateOfBirth) + .NotNull() + .LessThanOrEqualTo(DateTime.UtcNow) + .WithMessage(DateOfBirthValidationMessage); + } + } + + public class StaffValidator : AbstractValidator, IValidator + { + public const string JobTitleValidationMessage = "Please specify a job title"; + + public StaffValidator() + { + this.RuleFor(x => x.JobTitle).NotEmpty().WithMessage(JobTitleValidationMessage); + this.RuleFor(x => x.Department).NotEmpty(); + } + + public ValidationResult Validate(Person instance) + { + return base.Validate(instance as Staff); + } + + public Task ValidateAsync(Person instance, CancellationToken cancellation = new()) + { + return base.ValidateAsync(instance as Staff, cancellation); + } + } +} \ No newline at end of file diff --git a/tests/MADE.Data.Validation.Tests/MADE.Data.Validation.Tests.csproj b/tests/MADE.Data.Validation.Tests/MADE.Data.Validation.Tests.csproj index d9ba4c0d..70bbbd86 100644 --- a/tests/MADE.Data.Validation.Tests/MADE.Data.Validation.Tests.csproj +++ b/tests/MADE.Data.Validation.Tests/MADE.Data.Validation.Tests.csproj @@ -1,21 +1,20 @@ - net5.0 - + net6.0 false - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + - + diff --git a/tests/MADE.Data.Validation.Tests/Tests/Base64ValidatorTests.cs b/tests/MADE.Data.Validation.Tests/Tests/Base64ValidatorTests.cs new file mode 100644 index 00000000..14de8c05 --- /dev/null +++ b/tests/MADE.Data.Validation.Tests/Tests/Base64ValidatorTests.cs @@ -0,0 +1,73 @@ +namespace MADE.Data.Validation.Tests.Tests +{ + using System; + using System.Diagnostics.CodeAnalysis; + using MADE.Data.Validation.Validators; + using NUnit.Framework; + using Shouldly; + + [ExcludeFromCodeCoverage] + [TestFixture] + public class Base64ValidatorTests + { + public class WhenValidating + { + [Test] + public void ShouldBeDirtyOnceValidated() + { + // Arrange + const string value = "Test"; + var validator = new Base64Validator(); + + // Act + validator.Validate(value); + + // Assert + validator.IsDirty.ShouldBe(true); + } + + [Test] + public void ShouldBeValidIfDefinedBase64String() + { + // Arrange + const string value = "VGVzdA=="; + var validator = new Base64Validator(); + + // Act + validator.Validate(value); + + // Assert + validator.IsInvalid.ShouldBe(false); + } + + [Test] + public void ShouldBeValidIfConvertedToBase64String() + { + // Arrange + const string decodedValue = "Test"; + string value = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(decodedValue)); + var validator = new Base64Validator(); + + // Act + validator.Validate(value); + + // Assert + validator.IsInvalid.ShouldBe(false); + } + + [Test] + public void ShouldBeInvalidIfNotBase64() + { + // Arrange + const string value = "Tes"; + var validator = new Base64Validator(); + + // Act + validator.Validate(value); + + // Assert + validator.IsInvalid.ShouldBe(true); + } + } + } +} \ No newline at end of file diff --git a/tests/MADE.Data.Validation.Tests/Tests/BetweenValidatorTests.cs b/tests/MADE.Data.Validation.Tests/Tests/BetweenValidatorTests.cs index 9fa73ae0..4a0f971c 100644 --- a/tests/MADE.Data.Validation.Tests/Tests/BetweenValidatorTests.cs +++ b/tests/MADE.Data.Validation.Tests/Tests/BetweenValidatorTests.cs @@ -10,7 +10,7 @@ namespace MADE.Data.Validation.Tests.Tests [TestFixture] public class BetweenValidatorTests { - public class WhenValidating + public class WhenValidatingInclusive { [Test] public void ShouldBeDirtyOnceValidated() @@ -65,5 +65,61 @@ public void ShouldBeInvalidIfValueOutsideRange(IComparable value, IComparable mi validator.IsInvalid.ShouldBe(true); } } + + public class WhenValidatingExclusive + { + [Test] + public void ShouldBeDirtyOnceValidated() + { + // Arrange + int value = 1; + var validator = new BetweenValidator(0, 2) { Inclusive = false }; + + // Act + validator.Validate(value); + + // Assert + validator.IsDirty.ShouldBe(true); + } + + [TestCase(1, 0, 2)] + [TestCase(1.0, 0.0, 2.0)] + [TestCase(1.0f, 0.0f, 2.0f)] + public void ShouldBeValidIfValueWithinRange(IComparable value, IComparable min, IComparable max) + { + // Arrange + var validator = new BetweenValidator(min, max) { Inclusive = false }; + + // Act + validator.Validate(value); + + // Assert + validator.IsInvalid.ShouldBe(false); + } + + [TestCase(0, 0, 2)] + [TestCase(-1, 0, 2)] + [TestCase(2, 0, 2)] + [TestCase(3, 0, 2)] + [TestCase(0.0, 0.0, 2.0)] + [TestCase(-1.0, 0.0, 2.0)] + [TestCase(2.0, 0.0, 2.0)] + [TestCase(3.0, 0.0, 2.0)] + [TestCase(0.0f, 0.0f, 2.0f)] + [TestCase(-1.0f, 0.0f, 2.0f)] + [TestCase(2.0f, 0.0f, 2.0f)] + [TestCase(3.0f, 0.0f, 2.0f)] + public void ShouldBeInvalidIfValueOutsideRange(IComparable value, IComparable min, IComparable max) + { + // Arrange + var validator = new BetweenValidator(min, max) { Inclusive = false }; + + // Act + validator.Validate(value); + + // Assert + validator.IsInvalid.ShouldBe(true); + } + } } } \ No newline at end of file diff --git a/tests/MADE.Data.Validation.Tests/Tests/GuidValidatorTests.cs b/tests/MADE.Data.Validation.Tests/Tests/GuidValidatorTests.cs new file mode 100644 index 00000000..14312d3f --- /dev/null +++ b/tests/MADE.Data.Validation.Tests/Tests/GuidValidatorTests.cs @@ -0,0 +1,72 @@ +namespace MADE.Data.Validation.Tests.Tests +{ + using System; + using System.Diagnostics.CodeAnalysis; + using MADE.Data.Validation.Validators; + using NUnit.Framework; + using Shouldly; + + [ExcludeFromCodeCoverage] + [TestFixture] + public class GuidValidatorTests + { + public class WhenValidating + { + [Test] + public void ShouldBeDirtyOnceValidated() + { + // Arrange + string value = "Test"; + var validator = new GuidValidator(); + + // Act + validator.Validate(value); + + // Assert + validator.IsDirty.ShouldBe(true); + } + + [Test] + public void ShouldBeValidIfGuidType() + { + // Arrange + var value = Guid.NewGuid(); + var validator = new GuidValidator(); + + // Act + validator.Validate(value); + + // Assert + validator.IsInvalid.ShouldBe(false); + } + + [Test] + public void ShouldBeValidIfStringGuid() + { + // Arrange + var value = "f39bc65d-dcb5-47f1-a3ba-51fb5f584fd9"; + var validator = new GuidValidator(); + + // Act + validator.Validate(value); + + // Assert + validator.IsInvalid.ShouldBe(false); + } + + [Test] + public void ShouldBeInvalidIfNotGuid() + { + // Arrange + const string value = "Test"; + var validator = new GuidValidator(); + + // Act + validator.Validate(value); + + // Assert + validator.IsInvalid.ShouldBe(true); + } + } + } +} \ No newline at end of file diff --git a/tests/MADE.Data.Validation.Tests/Tests/LatitudeValidatorTests.cs b/tests/MADE.Data.Validation.Tests/Tests/LatitudeValidatorTests.cs new file mode 100644 index 00000000..a88dc485 --- /dev/null +++ b/tests/MADE.Data.Validation.Tests/Tests/LatitudeValidatorTests.cs @@ -0,0 +1,57 @@ +namespace MADE.Data.Validation.Tests.Tests +{ + using System.Diagnostics.CodeAnalysis; + using MADE.Data.Validation.Validators; + using NUnit.Framework; + using Shouldly; + + [ExcludeFromCodeCoverage] + [TestFixture] + public class LatitudeValidatorTests + { + public class WhenValidating + { + [Test] + public void ShouldBeDirtyOnceValidated() + { + // Arrange + string value = "Test"; + var validator = new LatitudeValidator(); + + // Act + validator.Validate(value); + + // Assert + validator.IsDirty.ShouldBe(true); + } + + [Test] + public void ShouldBeValidIfInLatitudeRange() + { + // Arrange + var validator = new LatitudeValidator(); + + // Act & Assert + for (var i = -90; i <= 90; i++) + { + validator.Validate(i); + validator.IsInvalid.ShouldBe(false); + } + } + + [TestCase(-90.5)] + [TestCase(90.5)] + public void ShouldBeInvalidIfOutLatitudeRange(object value) + { + // Arrange + var validator = new LatitudeValidator(); + + // Act + validator.Validate(value); + + // Assert + validator.IsInvalid.ShouldBe(true); + } + } + } +} \ No newline at end of file diff --git a/tests/MADE.Data.Validation.Tests/Tests/LongitudeValidatorTests.cs b/tests/MADE.Data.Validation.Tests/Tests/LongitudeValidatorTests.cs new file mode 100644 index 00000000..b29da2e1 --- /dev/null +++ b/tests/MADE.Data.Validation.Tests/Tests/LongitudeValidatorTests.cs @@ -0,0 +1,57 @@ +namespace MADE.Data.Validation.Tests.Tests +{ + using System.Diagnostics.CodeAnalysis; + using MADE.Data.Validation.Validators; + using NUnit.Framework; + using Shouldly; + + [ExcludeFromCodeCoverage] + [TestFixture] + public class LongitudeValidatorTests + { + public class WhenValidating + { + [Test] + public void ShouldBeDirtyOnceValidated() + { + // Arrange + string value = "Test"; + var validator = new LongitudeValidator(); + + // Act + validator.Validate(value); + + // Assert + validator.IsDirty.ShouldBe(true); + } + + [Test] + public void ShouldBeValidIfInLongitudeRange() + { + // Arrange + var validator = new LongitudeValidator(); + + // Act & Assert + for (var i = -180; i <= 180; i++) + { + validator.Validate(i); + validator.IsInvalid.ShouldBe(false); + } + } + + [TestCase(-180.5)] + [TestCase(180.5)] + public void ShouldBeInvalidIfOutLongitudeRange(object value) + { + // Arrange + var validator = new LongitudeValidator(); + + // Act + validator.Validate(value); + + // Assert + validator.IsInvalid.ShouldBe(true); + } + } + } +} \ No newline at end of file diff --git a/tests/MADE.Data.Validation.Tests/Tests/MacAddressValidatorTests.cs b/tests/MADE.Data.Validation.Tests/Tests/MacAddressValidatorTests.cs new file mode 100644 index 00000000..ce7a75c8 --- /dev/null +++ b/tests/MADE.Data.Validation.Tests/Tests/MacAddressValidatorTests.cs @@ -0,0 +1,61 @@ +namespace MADE.Data.Validation.Tests.Tests +{ + using System.Diagnostics.CodeAnalysis; + using MADE.Data.Validation.Validators; + using NUnit.Framework; + using Shouldly; + + [ExcludeFromCodeCoverage] + [TestFixture] + public class MacAddressValidatorTests + { + public class WhenValidating + { + [Test] + public void ShouldBeDirtyOnceValidated() + { + // Arrange + string value = "Test"; + var validator = new MacAddressValidator(); + + // Act + validator.Validate(value); + + // Assert + validator.IsDirty.ShouldBe(true); + } + + [TestCase("001122334455")] + [TestCase("00-11-22-33-44-55")] + [TestCase("0011.2233.4455")] + [TestCase("00:11:22:33:44:55")] + [TestCase("F0-E1-D2-C3-B4-A5")] + [TestCase("f0-e1-d2-c3-b4-a5")] + public void ShouldBeValidIfValidMacAddress(string value) + { + // Arrange + var validator = new MacAddressValidator(); + + // Act + validator.Validate(value); + + // Assert + validator.IsInvalid.ShouldBe(false); + } + + [TestCase("Test")] + [TestCase("00/11/22/33/44/55")] + public void ShouldBeInvalidIfInvalidMacAddress(string value) + { + // Arrange + var validator = new MacAddressValidator(); + + // Act + validator.Validate(value); + + // Assert + validator.IsInvalid.ShouldBe(true); + } + } + } +} \ No newline at end of file diff --git a/tests/MADE.Data.Validation.Tests/Tests/PredicateValidatorTests.cs b/tests/MADE.Data.Validation.Tests/Tests/PredicateValidatorTests.cs new file mode 100644 index 00000000..9e0474d0 --- /dev/null +++ b/tests/MADE.Data.Validation.Tests/Tests/PredicateValidatorTests.cs @@ -0,0 +1,54 @@ +namespace MADE.Data.Validation.Tests.Tests +{ + using System.Diagnostics.CodeAnalysis; + using MADE.Data.Validation.Validators; + using NUnit.Framework; + using Shouldly; + + [ExcludeFromCodeCoverage] + [TestFixture] + public class PredicateValidatorTests + { + public class WhenValidating + { + [Test] + public void ShouldBeDirtyOnceValidated() + { + // Arrange + var validator = new PredicateValidator(i => i > 0); + + // Act + validator.Validate(1); + + // Assert + validator.IsDirty.ShouldBe(true); + } + + [Test] + public void ShouldBeValidIfPredicateIsTrue() + { + // Arrange + var validator = new PredicateValidator(i => i > 0); + + // Act + validator.Validate(1); + + // Assert + validator.IsInvalid.ShouldBe(false); + } + + [Test] + public void ShouldBeInvalidIfPredicateIsFalse() + { + // Arrange + var validator = new PredicateValidator(i => i > 0); + + // Act + validator.Validate(0); + + // Assert + validator.IsInvalid.ShouldBe(true); + } + } + } +} \ No newline at end of file diff --git a/tests/MADE.Data.Validation.Tests/Tests/WellFormedUrlValidatorTests.cs b/tests/MADE.Data.Validation.Tests/Tests/WellFormedUrlValidatorTests.cs new file mode 100644 index 00000000..b2d398a0 --- /dev/null +++ b/tests/MADE.Data.Validation.Tests/Tests/WellFormedUrlValidatorTests.cs @@ -0,0 +1,78 @@ +namespace MADE.Data.Validation.Tests.Tests +{ + using System; + using System.Diagnostics.CodeAnalysis; + using MADE.Data.Validation.Validators; + using NUnit.Framework; + using Shouldly; + + [ExcludeFromCodeCoverage] + [TestFixture] + public class WellFormedUrlValidatorTests + { + public class WhenValidating + { + private static readonly object[] ValidUrls = + { + "https://www.wellformed.com", "http://www.wellformed.com", "ftp://wellformed.com", + "https://www.wellformed.com/slug" + }; + + [Test] + public void ShouldBeDirtyOnceValidated() + { + // Arrange + string value = "www.website.com"; + var validator = new WellFormedUrlValidator(); + + // Act + validator.Validate(value); + + // Assert + validator.IsDirty.ShouldBe(true); + } + + [TestCaseSource(nameof(ValidUrls))] + public void ShouldBeValidIfWellFormedUrlString(string value) + { + // Arrange + var validator = new WellFormedUrlValidator(); + + // Act + validator.Validate(value); + + // Assert + validator.IsInvalid.ShouldBe(false); + } + + [TestCaseSource(nameof(ValidUrls))] + public void ShouldBeValidIfWellFormedUri(string value) + { + // Arrange + var uri = new Uri(value); + var validator = new WellFormedUrlValidator(); + + // Act + validator.Validate(uri); + + // Assert + validator.IsInvalid.ShouldBe(false); + } + + [TestCase("NotUrl")] + [TestCase("www.notwellformed.com")] + [TestCase("www.notwellformed.com/slug")] + public void ShouldBeInvalidIfNotWellFormedUrlString(string value) + { + // Arrange + var validator = new WellFormedUrlValidator(); + + // Act + validator.Validate(value); + + // Assert + validator.IsInvalid.ShouldBe(true); + } + } + } +} \ No newline at end of file diff --git a/tests/MADE.Diagnostics.Tests/MADE.Diagnostics.Tests.csproj b/tests/MADE.Diagnostics.Tests/MADE.Diagnostics.Tests.csproj index b27c70b5..9e37b65e 100644 --- a/tests/MADE.Diagnostics.Tests/MADE.Diagnostics.Tests.csproj +++ b/tests/MADE.Diagnostics.Tests/MADE.Diagnostics.Tests.csproj @@ -1,21 +1,20 @@ - net5.0 - + net6.0 false - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + - + diff --git a/tests/MADE.Networking.Tests/MADE.Networking.Tests.csproj b/tests/MADE.Networking.Tests/MADE.Networking.Tests.csproj index 2afd97f2..9a2c9284 100644 --- a/tests/MADE.Networking.Tests/MADE.Networking.Tests.csproj +++ b/tests/MADE.Networking.Tests/MADE.Networking.Tests.csproj @@ -1,21 +1,20 @@ - net5.0 - + net6.0 false - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + - + diff --git a/tests/MADE.Web.Tests/MADE.Web.Tests.csproj b/tests/MADE.Web.Tests/MADE.Web.Tests.csproj index 9f73c485..2ad7e570 100644 --- a/tests/MADE.Web.Tests/MADE.Web.Tests.csproj +++ b/tests/MADE.Web.Tests/MADE.Web.Tests.csproj @@ -1,21 +1,20 @@ - net5.0 - + net6.0 false - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + - +